Initial commit

This commit is contained in:
2025-11-20 23:21:05 +08:00
commit 13bc6db529
378 changed files with 54527 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
<?php
namespace App\Http\Controllers;
use App\Models\Issue;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class IssueReportsController extends Controller
{
public function index(Request $request)
{
// Date range filter (default: last 30 days)
$startDate = $request->date('start_date', now()->subDays(30));
$endDate = $request->date('end_date', now());
// Overview Statistics
$stats = [
'total_issues' => Issue::count(),
'open_issues' => Issue::open()->count(),
'closed_issues' => Issue::closed()->count(),
'overdue_issues' => Issue::overdue()->count(),
];
// Issues by Status
$issuesByStatus = Issue::select('status', DB::raw('count(*) as count'))
->groupBy('status')
->get()
->mapWithKeys(fn($item) => [$item->status => $item->count]);
// Issues by Priority
$issuesByPriority = Issue::select('priority', DB::raw('count(*) as count'))
->groupBy('priority')
->get()
->mapWithKeys(fn($item) => [$item->priority => $item->count]);
// Issues by Type
$issuesByType = Issue::select('issue_type', DB::raw('count(*) as count'))
->groupBy('issue_type')
->get()
->mapWithKeys(fn($item) => [$item->issue_type => $item->count]);
// Issues Created Over Time (last 30 days)
$issuesCreatedOverTime = Issue::select(
DB::raw('DATE(created_at) as date'),
DB::raw('count(*) as count')
)
->whereBetween('created_at', [$startDate, $endDate])
->groupBy('date')
->orderBy('date')
->get();
// Issues Closed Over Time (last 30 days)
$issuesClosedOverTime = Issue::select(
DB::raw('DATE(closed_at) as date'),
DB::raw('count(*) as count')
)
->whereNotNull('closed_at')
->whereBetween('closed_at', [$startDate, $endDate])
->groupBy('date')
->orderBy('date')
->get();
// Assignee Performance
$assigneePerformance = User::select('users.id', 'users.name')
->leftJoin('issues', 'users.id', '=', 'issues.assigned_to_user_id')
->selectRaw('count(issues.id) as total_assigned')
->selectRaw('sum(case when issues.status = ? then 1 else 0 end) as completed', [Issue::STATUS_CLOSED])
->selectRaw('sum(case when issues.due_date < NOW() and issues.status != ? then 1 else 0 end) as overdue', [Issue::STATUS_CLOSED])
->groupBy('users.id', 'users.name')
->having('total_assigned', '>', 0)
->orderByDesc('total_assigned')
->limit(10)
->get()
->map(function ($user) {
$user->completion_rate = $user->total_assigned > 0
? round(($user->completed / $user->total_assigned) * 100, 1)
: 0;
return $user;
});
// Time Tracking Metrics
$timeTrackingMetrics = Issue::selectRaw('
sum(estimated_hours) as total_estimated,
sum(actual_hours) as total_actual,
avg(estimated_hours) as avg_estimated,
avg(actual_hours) as avg_actual
')
->whereNotNull('estimated_hours')
->first();
// Top Labels Used
$topLabels = DB::table('issue_labels')
->select('issue_labels.id', 'issue_labels.name', 'issue_labels.color', DB::raw('count(issue_label_pivot.issue_id) as usage_count'))
->leftJoin('issue_label_pivot', 'issue_labels.id', '=', 'issue_label_pivot.issue_label_id')
->groupBy('issue_labels.id', 'issue_labels.name', 'issue_labels.color')
->having('usage_count', '>', 0)
->orderByDesc('usage_count')
->limit(10)
->get();
// Average Resolution Time (days)
$avgResolutionTime = Issue::whereNotNull('closed_at')
->selectRaw('avg(TIMESTAMPDIFF(DAY, created_at, closed_at)) as avg_days')
->value('avg_days');
// Recent Activity (last 10 issues)
$recentIssues = Issue::with(['creator', 'assignee'])
->latest()
->limit(10)
->get();
return view('admin.issue-reports.index', compact(
'stats',
'issuesByStatus',
'issuesByPriority',
'issuesByType',
'issuesCreatedOverTime',
'issuesClosedOverTime',
'assigneePerformance',
'timeTrackingMetrics',
'topLabels',
'avgResolutionTime',
'recentIssues',
'startDate',
'endDate'
));
}
}