Files
HRM-System/app/Http/Controllers/DashboardController.php
2026-04-13 08:16:56 +08:00

550 lines
21 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Announcement;
use App\Models\AttendanceRecord;
use App\Models\Branch;
use App\Models\Candidate;
use App\Models\Department;
use App\Models\Employee;
use App\Models\JobPosting;
use App\Models\LeaveApplication;
use App\Models\LeaveType;
use App\Models\Meeting;
use App\Models\Coupon;
use App\Models\Plan;
use App\Models\PlanOrder;
use App\Models\PlanRequest;
use App\Models\Shift;
use App\Models\User;
use Inertia\Inertia;
class DashboardController extends Controller
{
public function index()
{
$user = auth()->user();
// Super admin always gets dashboard
if ($user->type === 'superadmin' || $user->type === 'super admin') {
return $this->renderDashboard();
}
// Check if user has dashboard permission (skip if permission doesn't exist)
try {
if ($user->hasPermissionTo('manage-dashboard')) {
return $this->renderDashboard();
}
} catch (\Exception $e) {
// Permission doesn't exist, continue to dashboard for authenticated users
return $this->renderDashboard();
}
// Redirect to first available page
return $this->redirectToFirstAvailablePage();
}
public function redirectToFirstAvailablePage()
{
$user = auth()->user();
// Define available routes with their permissions
$routes = [
['route' => 'users.index', 'permission' => 'manage-users'],
['route' => 'roles.index', 'permission' => 'manage-roles'],
['route' => 'plans.index', 'permission' => 'manage-plans'],
['route' => 'referral.index', 'permission' => 'manage-referral'],
['route' => 'settings.index', 'permission' => 'manage-settings'],
];
// Find first available route
foreach ($routes as $routeData) {
if ($user->hasPermissionTo($routeData['permission'])) {
return redirect()->route($routeData['route']);
}
}
// If no permissions found, logout user
auth()->logout();
return redirect()->route('login')->with('error', __('No access permissions found.'));
}
private function renderDashboard()
{
$user = auth()->user();
if ($user->type === 'superadmin' || $user->type === 'super admin') {
return $this->renderSuperAdminDashboard();
} else {
return $this->renderCompanyDashboard();
}
}
private function renderSuperAdminDashboard()
{
// Get system-wide statistics
$totalCompanies = User::where('type', 'company')->count();
$totalUsers = User::where('type', '!=', 'superadmin')->where('type', '!=', 'super admin')->count();
$totalRevenue = PlanOrder::where('status', 'approved')->sum('final_price') ?? 0;
$activePlans = Plan::where('is_plan_enable', 'on')->count();
$pendingRequests = PlanRequest::where('status', 'pending')->count();
$activeCoupons = Coupon::where('status', true)->count();
// Calculate monthly growth for companies
$currentMonthCompanies = User::where('type', 'company')
->whereMonth('created_at', now()->month)
->whereYear('created_at', now()->year)
->count();
$previousMonthCompanies = User::where('type', 'company')
->whereMonth('created_at', now()->subMonth()->month)
->whereYear('created_at', now()->subMonth()->year)
->count();
$monthlyGrowth = isDemo() ? 90 : ($previousMonthCompanies > 0
? round((($currentMonthCompanies - $previousMonthCompanies) / $previousMonthCompanies) * 100, 1)
: ($currentMonthCompanies > 0 ? 100 : 0));
$dashboardData = [
'stats' => [
'totalCompanies' => $totalCompanies,
'totalUsers' => $totalUsers,
'totalRevenue' => $totalRevenue,
'activePlans' => $activePlans,
'pendingRequests' => $pendingRequests,
'monthlyGrowth' => $monthlyGrowth,
'activeCoupons' => $activeCoupons,
],
'recentActivity' => User::where('type', 'company')
->orderBy('created_at', 'desc')
->take(5)
->get(['id', 'name', 'email', 'created_at'])
->map(function ($company) {
return [
'id' => $company->id,
'name' => $company->name,
'email' => $company->email,
'registered_at' => $company->created_at->diffForHumans(),
'status' => 'active',
];
}),
'topPlans' => Plan::withCount('users')
->orderBy('users_count', 'desc')
->take(5)
->get()
->map(function ($plan) {
return [
'name' => $plan->name,
'subscribers' => $plan->users_count,
'revenue' => $plan->users_count * $plan->price,
];
}),
];
return Inertia::render('superadmin/dashboard', props: [
'dashboardData' => $dashboardData,
]);
}
private function renderCompanyDashboard()
{
$user = auth()->user();
// If user is employee, show limited dashboard
if ($user->type === 'employee') {
return $this->renderEmployeeDashboard();
}
$companyUserIds = $this->getCompanyUserIds();
// Core HR Statistics
$totalEmployees = User::where('type', 'employee')->whereIn('created_by', $companyUserIds)->count();
$totalBranches = Branch::whereIn('created_by', $companyUserIds)->count();
$totalDepartments = Department::whereIn('created_by', $companyUserIds)->count();
// Monthly Statistics
if (isDemo()) {
$newEmployeesThisMonth = Employee::whereIn('created_by', $companyUserIds)->count();
$jobPostsThisMonth = JobPosting::whereIn('created_by', $companyUserIds)->count();
$candidatesThisMonth = Candidate::whereIn('created_by', $companyUserIds)->count();
} else {
$newEmployeesThisMonth = Employee::whereIn('created_by', $companyUserIds)
->whereMonth('created_at', now()->month)->count();
$jobPostsThisMonth = JobPosting::whereIn('created_by', $companyUserIds)
->whereMonth('created_at', now()->month)->count();
$candidatesThisMonth = Candidate::whereIn('created_by', $companyUserIds)
->whereMonth('created_at', now()->month)->count();
}
// Attendance Statistics
if (isDemo()) {
$presentToday = 45;
$attendanceRate = 85.5;
} else {
$presentToday = AttendanceRecord::whereIn('created_by', $companyUserIds)
->whereDate('date', today())->where('status', 'present')->count();
$attendanceRate = $totalEmployees > 0 ? round(($presentToday / $totalEmployees) * 100, 1) : 0;
}
// Leave Statistics
$pendingLeaves = LeaveApplication::whereIn('created_by', $companyUserIds)
->where('status', 'pending')->count();
$onLeaveToday = LeaveApplication::whereIn('created_by', $companyUserIds)
->where('status', 'approved');
$onLeaveToday = $onLeaveToday->whereDate('start_date', '<=', today())
->whereDate('end_date', '>=', today())->count();
// Recruitment Statistics
$activeJobPostings = JobPosting::whereIn('created_by', $companyUserIds)
->where('status', 'Published')->count();
$totalCandidates = Candidate::whereIn('created_by', $companyUserIds)->count();
// Department Distribution for Chart
// $predefinedColors = ['#4F46E5', '#10b77f', '#F59E0B', '#EF4444', '#3B82F6', '#D946EF'];
$predefinedColors = ['#0EA5E9', '#14B8A6', '#6366F1', '#0D9488', '#7C3AED', '#0369A1'];
$departmentStats = Department::whereIn('created_by', $companyUserIds)
->withCount('employees')
->with('branch')
->orderBy('employees_count', 'desc')
->when(config('app.is_demo') == true, function ($query) {
return $query->take(6);
})
->get()
->map(function ($dept, $index) use ($predefinedColors) {
$displayName = $dept->name . ' (' . $dept->branch->name . ')';
return [
'name' => $displayName,
'value' => $dept->employees_count,
'color' => config('app.is_demo') == true
? ($predefinedColors[$index] ?? '#' . substr(md5($displayName), 0, 6))
: '#' . substr(md5($displayName), 0, 6),
];
});
// Monthly Hiring Trend for Chart (last 6 months)
if (isDemo()) {
$hiringTrend = [
['month' => now()->subMonths(5)->format('M Y'), 'hires' => 8],
['month' => now()->subMonths(4)->format('M Y'), 'hires' => 12],
['month' => now()->subMonths(3)->format('M Y'), 'hires' => 15],
['month' => now()->subMonths(2)->format('M Y'), 'hires' => 10],
['month' => now()->subMonths(1)->format('M Y'), 'hires' => 18],
['month' => now()->format('M Y'), 'hires' => 14],
];
} else {
$hiringTrend = [];
for ($i = 5; $i >= 0; $i--) {
$month = now()->subMonths($i);
$count = Employee::whereIn('created_by', $companyUserIds)
->whereMonth('created_at', $month->month)
->whereYear('created_at', $month->year)
->count();
$hiringTrend[] = [
'month' => $month->format('M Y'),
'hires' => $count,
];
}
}
// Candidate Status Distribution for Chart
$candidateStatusStats = Candidate::whereIn('created_by', $companyUserIds)
->selectRaw('status, COUNT(*) as count')
->groupBy('status')
->get()
->map(function ($item) {
$colors = [
'New' => '#0EA5E9',
'Screening' => '#F59E0B',
'Interview' => '#8B5CF6',
'Offer' => '#14B8A6',
'Hired' => '#10B981',
'Rejected' => '#EF4444',
];
return [
'name' => $item->status,
'value' => $item->count,
'color' => $colors[$item->status] ?? '#6b7280',
];
});
// Leave Types for Chart
$leaveTypesStats = LeaveType::whereIn('created_by', $companyUserIds)
->get()
->map(function ($leaveType) {
return [
'name' => $leaveType->name,
'value' => $leaveType->max_days_per_year,
'color' => $leaveType->color ?: '#' . substr(md5($leaveType->name), 0, 6),
];
});
// Employee Growth Chart (Monthly for current year)
if (isDemo()) {
$employeeGrowthChart = [
['month' => 'January', 'employees' => 15],
['month' => 'February', 'employees' => 5],
['month' => 'March', 'employees' => 22],
['month' => 'April', 'employees' => 10],
['month' => 'May', 'employees' => 28],
['month' => 'June', 'employees' => 32],
['month' => 'July', 'employees' => 35],
['month' => 'August', 'employees' => 50],
['month' => 'September', 'employees' => 42],
['month' => 'October', 'employees' => 45],
['month' => 'November', 'employees' => 48],
['month' => 'December', 'employees' => 52],
];
} else {
$employeeGrowthChart = [];
for ($month = 1; $month <= 12; $month++) {
$count = User::where('type', 'employee')
->whereIn('created_by', $companyUserIds)
->whereMonth('created_at', $month)
->whereYear('created_at', now()->year)
->count();
$employeeGrowthChart[] = [
'month' => date('F', mktime(0, 0, 0, $month, 1)),
'employees' => $count,
];
}
}
// Recent Activities
$recentLeaves = LeaveApplication::whereIn('created_by', $companyUserIds)
->with(['employee', 'leaveType']);
if (config('app.is_demo') == true) {
$recentLeaves = $recentLeaves->whereIn('status', ['approved', 'absent'])->get();
} else {
$recentLeaves = $recentLeaves->whereIn('status', ['approved', 'absent'])
->whereDate('start_date', '<=', today())
->whereDate('end_date', '>=', today())
->get();
}
$recentCandidates = Candidate::whereIn('created_by', $companyUserIds)
->with(['job'])
->orderBy('created_at', 'desc')
->take(5)
->get();
// Recent Announcements
$recentAnnouncements = Announcement::whereIn('created_by', $companyUserIds)
->orderBy('created_at', 'desc')
->take(5)
->get();
// Recent Meetings
$recentMeetings = Meeting::whereIn('created_by', $companyUserIds)
->orderBy('created_at', 'desc')
->take(5)
->get();
$dashboardData = [
'stats' => [
'totalEmployees' => $totalEmployees,
'totalBranches' => $totalBranches,
'totalDepartments' => $totalDepartments,
'newEmployeesThisMonth' => $newEmployeesThisMonth,
'jobPostsThisMonth' => $jobPostsThisMonth,
'candidatesThisMonth' => $candidatesThisMonth,
'attendanceRate' => $attendanceRate,
'presentToday' => $presentToday,
'pendingLeaves' => $pendingLeaves,
'onLeaveToday' => $onLeaveToday,
'activeJobPostings' => $activeJobPostings,
'totalCandidates' => $totalCandidates,
],
'charts' => [
'departmentStats' => $departmentStats,
'hiringTrend' => $hiringTrend,
'candidateStatusStats' => $candidateStatusStats,
'leaveTypesStats' => $leaveTypesStats,
'employeeGrowthChart' => $employeeGrowthChart,
],
'recentActivities' => [
'leaves' => $recentLeaves,
'candidates' => $recentCandidates,
'announcements' => $recentAnnouncements,
'meetings' => $recentMeetings,
],
'userType' => $user->type,
];
return Inertia::render('dashboard', [
'dashboardData' => $dashboardData,
]);
}
private function renderEmployeeDashboard()
{
$user = auth()->user();
$companyUserIds = $this->getCompanyUserIds();
// Recent Announcements
$recentAnnouncements = \App\Models\Announcement::whereIn('created_by', $companyUserIds)
->orderBy('created_at', 'desc')
->take(5)
->get();
// Recent Meetings - get meetings where user is organizer
$recentMeetings = \App\Models\Meeting::with('attendees')
->whereIn('created_by', $companyUserIds)
->where('organizer_id', $user->id)
->orderBy('created_at', 'desc')
->get();
// Get meetings where user is attendee
$meetingAttendee = \App\Models\MeetingAttendee::with('meeting')
->where('user_id', $user->id)
->get();
// Extract meetings from attendee records
$attendeeMeetings = $meetingAttendee->pluck(value: 'meeting')->filter();
// Merge and remove duplicates
$recentMeetings = $recentMeetings->merge($attendeeMeetings)
->unique('id')
->filter(function ($meeting) {
return $meeting->meeting_date >= today();
})
->sortByDesc('created_at')
->values();
// Employee Stats
$totalAwards = \App\Models\Award::where('employee_id', $user->id)->count();
$totalWarnings = \App\Models\Warning::where('employee_id', $user->id)->count();
$totalComplaints = \App\Models\Complaint::where('against_employee_id', $user->id)->count();
// Get shifts and attendance policies for clock in functionality
$shifts = \App\Models\Shift::whereIn('created_by', $companyUserIds)
->where('status', 'active')
->get(['id', 'name', 'start_time', 'end_time']);
$attendancePolicies = \App\Models\AttendancePolicy::whereIn('created_by', $companyUserIds)
->where('status', 'active')
->get(['id', 'name']);
// Get today's attendance for the employee
$todayAttendance = AttendanceRecord::where('employee_id', $user->id)
->where('date', \Carbon\Carbon::today())
->first();
// Get employee's assigned shift
$employeeShift = null;
$employee = Employee::where('user_id', $user->id)->first();
if ($employee && $employee->shift_id) {
$employeeShift = Shift::find($employee->shift_id);
}
// Auto clock out previous days like yesterday and alll thing if not clocked out
$previousAttendance = AttendanceRecord::where('employee_id', $user->id)
->where('date', '<', \Carbon\Carbon::today())
->whereNotNull('clock_in')
->whereNull('clock_out')
->get();
foreach ($previousAttendance as $record) {
$recordDate = \Carbon\Carbon::parse($record->date);
$shift = Shift::find($record->shift_id) ?? $employeeShift;
if ($shift) {
$record->update([
'clock_out' => $shift->end_time,
]);
if (method_exists($record, 'processAttendance')) {
$record->processAttendance();
}
}
}
// Auto clock out if shift end time has passed for today
// if ($todayAttendance && $todayAttendance->clock_in && !$todayAttendance->clock_out && $employeeShift) {
// $now = \Carbon\Carbon::now();
// $shiftEndTime = \Carbon\Carbon::today()->setTimeFromTimeString($employeeShift->end_time);
// if ($now->greaterThan($shiftEndTime)) {
// $todayAttendance->update([
// 'clock_out' => $employeeShift->end_time,
// ]);
// if (method_exists($todayAttendance, 'processAttendance')) {
// $todayAttendance->processAttendance();
// }
// $todayAttendance = $todayAttendance->fresh();
// }
// }
$dashboardData = [
'stats' => [
'totalAwards' => $totalAwards,
'totalWarnings' => $totalWarnings,
'totalComplaints' => $totalComplaints,
],
'recentActivities' => [
'announcements' => $recentAnnouncements,
'meetings' => $recentMeetings,
],
'shifts' => $shifts,
'attendancePolicies' => $attendancePolicies,
'todayAttendance' => $todayAttendance,
'currentTime' => \Carbon\Carbon::now()->format('H:i:s'),
'employeeShift' => $employeeShift,
'userType' => $user->type,
];
return Inertia::render('employee-dashboard', [
'dashboardData' => $dashboardData,
]);
}
// private function getCompanyUserIds()
// {
// $user = auth()->user();
// if ($user->type === 'company') {
// $companyUserIds = User::where('created_by', $user->id)->pluck('id')->toArray();
// $companyUserIds[] = $user->id;
// return $companyUserIds;
// } else {
// $userCreatedBy = User::where('id', $user->created_by)->value('id');
// $companyUserIds = User::where('created_by', $userCreatedBy)->pluck('id')->toArray();
// $companyUserIds[] = $userCreatedBy;
// return $companyUserIds;
// }
// }
private function getCompanyUserIds()
{
$user = auth()->user();
if ($user->type === 'company') {
$companyId = getCompanyId($user->id);
if ($companyId) {
$allUsers = getAllCompanyUsers($companyId);
$allUsers[] = $companyId; // Include company itself
return array_unique($allUsers);
}
return [];
} else {
$companyId = getCompanyId($user->id);
if ($companyId) {
$allUsers = getAllCompanyUsers($companyId);
$allUsers[] = $companyId; // Include company itself
return array_unique($allUsers);
}
return [];
}
}
}