Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 176 additions & 0 deletions app/Http/Controllers/DashboardController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\User;
use App\Models\Project;
use App\Models\Task;
use App\Models\Note;
use App\Models\Reminder;
use App\Models\Routine;
use App\Models\File;
use Carbon\Carbon;

class DashboardController extends Controller
{
/**
* Show the dashboard.
*/
public function index()
{
$user = Auth::user();

// Basic counts
$tasksCount = $user->tasks()->count();
$routinesCount = $user->routines()->count();
$notesCount = $user->notes()->count();
$remindersCount = $user->reminders()->count();
$filesCount = $user->files()->count();
$projectsCount = $user->projects()->count();

// Recent items
$recentTasks = $user->tasks()
->with('project')
->latest()
->take(5)
->get();

$recentNotes = $user->notes()
->latest()
->take(5)
->get();

// Today's routines (based on current day and time)
$today = now()->format('l'); // Full day name (Monday, Tuesday, etc.)
$todayRoutines = $user->routines()
->where('frequency', 'daily')
->whereJsonContains('days', strtolower($today))
->get();

// Upcoming reminders
$upcomingReminders = $user->reminders()
->where('date', '>=', now())
->orderBy('date')
->take(5)
->get();

// Additional statistics for analytics
$completedTasksThisWeek = $user->tasks()
->where('status', 'completed')
->whereDate('updated_at', '>=', now()->startOfWeek())
->count();

$totalTasks = max($user->tasks()->count(), 1);
$completedTasks = $user->tasks()->where('status', 'completed')->count();
$completionRate = round(($completedTasks / $totalTasks) * 100);

$activeProjects = $user->projects()
->where('status', 'in_progress')
->count();

$overdueTasks = $user->tasks()
->where('due_date', '<', now())
->where('status', '!=', 'completed')
->count();

// Task status distribution
$taskStatusDistribution = [
'to_do' => $user->tasks()->where('status', 'to_do')->count(),
'in_progress' => $user->tasks()->where('status', 'in_progress')->count(),
'completed' => $user->tasks()->where('status', 'completed')->count(),
];

// Priority distribution (only non-completed tasks)
$priorityDistribution = [
'high' => $user->tasks()->where('priority', 'high')->where('status', '!=', 'completed')->count(),
'medium' => $user->tasks()->where('priority', 'medium')->where('status', '!=', 'completed')->count(),
'low' => $user->tasks()->where('priority', 'low')->where('status', '!=', 'completed')->count(),
];

// Calculate priority percentages for progress bars
$totalNonCompletedTasks = max($user->tasks()->where('status', '!=', 'completed')->count(), 1);
$priorityPercentages = [
'high' => round(($priorityDistribution['high'] / $totalNonCompletedTasks) * 100),
'medium' => round(($priorityDistribution['medium'] / $totalNonCompletedTasks) * 100),
'low' => round(($priorityDistribution['low'] / $totalNonCompletedTasks) * 100),
];

return view('dashboard', compact(
'tasksCount',
'routinesCount',
'notesCount',
'remindersCount',
'filesCount',
'projectsCount',
'recentTasks',
'todayRoutines',
'recentNotes',
'upcomingReminders',
'completedTasksThisWeek',
'completionRate',
'activeProjects',
'overdueTasks',
'taskStatusDistribution',
'priorityDistribution',
'priorityPercentages'
));
}

/**
* Get productivity data for charts (AJAX endpoint).
*/
public function getProductivityData(Request $request)
{
$user = Auth::user();
$period = $request->get('period', 'week');

$data = [];
$labels = [];

switch ($period) {
case 'week':
$startDate = now()->startOfWeek();
for ($i = 0; $i < 7; $i++) {
$date = $startDate->copy()->addDays($i);
$labels[] = $date->format('M j');
$data[] = $user->tasks()
->where('status', 'completed')
->whereDate('updated_at', $date)
->count();
}
break;

case 'month':
$startDate = now()->startOfMonth();
$daysInMonth = now()->daysInMonth;
for ($i = 0; $i < $daysInMonth; $i++) {
$date = $startDate->copy()->addDays($i);
$labels[] = $date->format('j');
$data[] = $user->tasks()
->where('status', 'completed')
->whereDate('updated_at', $date)
->count();
}
break;

case 'year':
for ($i = 0; $i < 12; $i++) {
$date = now()->startOfYear()->addMonths($i);
$labels[] = $date->format('M');
$data[] = $user->tasks()
->where('status', 'completed')
->whereYear('updated_at', now()->year)
->whereMonth('updated_at', $date->month)
->count();
}
break;
}

return response()->json([
'labels' => $labels,
'data' => $data
]);
}
}
129 changes: 122 additions & 7 deletions app/Http/Controllers/NoteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,171 @@
use App\Models\Note;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\JsonResponse;

class NoteController extends Controller
{
public function index()
public function index(Request $request)
{
$notes = Auth::user()->notes()->latest()->get();
return view('notes.index', compact('notes'));
$query = Auth::user()->notes()->latest();

// Handle search
if ($request->filled('search')) {
$query->search($request->search);
}

// Handle category filter
if ($request->filled('category') && $request->category !== 'all') {
$query->byCategory($request->category);
}

// Handle favorites filter
if ($request->filled('favorites') && $request->favorites == '1') {
$query->favorites();
}

$notes = $query->get();

// Get categories for filter
$categories = Auth::user()->notes()
->whereNotNull('category')
->where('category', '!=', '')
->distinct()
->pluck('category')
->sort();

if ($request->ajax()) {
return response()->json([
'html' => view('notes.partials.notes-grid', compact('notes'))->render(),
'count' => $notes->count()
]);
}

return view('notes.index', compact('notes', 'categories'));
}

public function create()
{
return view('notes.create');
$categories = Auth::user()->notes()
->whereNotNull('category')
->where('category', '!=', '')
->distinct()
->pluck('category')
->sort();

return view('notes.create', compact('categories'));
}

public function store(Request $request)
{
$request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
'category' => 'nullable|string|max:100',
'tags' => 'nullable|string',
'is_favorite' => 'boolean',
'date' => 'nullable|date',
'time' => 'nullable|date_format:H:i',
]);

Auth::user()->notes()->create($request->all());
$data = $request->all();

// Process tags
if ($request->filled('tags')) {
$data['tags'] = array_map('trim', explode(',', $request->tags));
}

Auth::user()->notes()->create($data);

return redirect()->route('notes.index')->with('success', 'Note created successfully.');
}

public function show(Note $note)
{
if ($note->user_id !== Auth::id()) {
abort(403);
}
return view('notes.show', compact('note'));
}

public function edit(Note $note)
{
return view('notes.edit', compact('note'));
if ($note->user_id !== Auth::id()) {
abort(403);
}

$categories = Auth::user()->notes()
->whereNotNull('category')
->where('category', '!=', '')
->distinct()
->pluck('category')
->sort();

return view('notes.edit', compact('note', 'categories'));
}

public function update(Request $request, Note $note)
{
if ($note->user_id !== Auth::id()) {
abort(403);
}

$request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
'category' => 'nullable|string|max:100',
'tags' => 'nullable|string',
'is_favorite' => 'boolean',
'date' => 'nullable|date',
'time' => 'nullable|date_format:H:i',
]);

$note->update($request->all());
$data = $request->all();

Comment on lines 117 to +128
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Same for update(): avoid $request->all().

-        $request->validate([
+        $validated = $request->validate([
             'title' => 'required|string|max:255',
             'content' => 'required|string',
             'category' => 'nullable|string|max:100',
             'tags' => 'nullable|string',
             'is_favorite' => 'boolean',
             'date' => 'nullable|date',
             'time' => 'nullable|date_format:H:i',
         ]);
-
-        $data = $request->all();
+        $data = $validated;
 
         // Process tags
         if ($request->filled('tags')) {
             $data['tags'] = array_map('trim', explode(',', $request->tags));
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
'category' => 'nullable|string|max:100',
'tags' => 'nullable|string',
'is_favorite' => 'boolean',
'date' => 'nullable|date',
'time' => 'nullable|date_format:H:i',
]);
$note->update($request->all());
$data = $request->all();
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
'category' => 'nullable|string|max:100',
'tags' => 'nullable|string',
'is_favorite' => 'boolean',
'date' => 'nullable|date',
'time' => 'nullable|date_format:H:i',
]);
$data = $validated;
// Process tags
if ($request->filled('tags')) {
$data['tags'] = array_map('trim', explode(',', $request->tags));
}
🤖 Prompt for AI Agents
In app/Http/Controllers/NoteController.php around lines 117 to 128, you're
calling $request->all() after validation which can include unexpected or
malicious input; replace it by using the validated data only. Capture the
validated array (e.g. $data = $request->validate([ ...rules... ]);) or
explicitly pull permitted fields ($data =
$request->only(['title','content','category','tags','is_favorite','date','time']));
apply the same change in update(): avoid $request->all(), use validated() /
validate() or only() to ensure only allowed fields are used.

// Process tags
if ($request->filled('tags')) {
$data['tags'] = array_map('trim', explode(',', $request->tags));
}

$note->update($data);

return redirect()->route('notes.index')->with('success', 'Note updated successfully.');
}

public function destroy(Note $note)
{
if ($note->user_id !== Auth::id()) {
abort(403);
}

$note->delete();
return redirect()->route('notes.index')->with('success', 'Note deleted successfully.');
}

public function toggleFavorite(Note $note): JsonResponse
{
if ($note->user_id !== Auth::id()) {
abort(403);
}

$note->update(['is_favorite' => !$note->is_favorite]);

return response()->json([
'success' => true,
'is_favorite' => $note->is_favorite
]);
}

public function duplicate(Note $note)
{
if ($note->user_id !== Auth::id()) {
abort(403);
}

$newNote = $note->replicate();
$newNote->title = $note->title . ' (Copy)';
$newNote->save();

return redirect()->route('notes.index')->with('success', 'Note duplicated successfully.');
}
}
Loading