550 lines
21 KiB
PHP
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 [];
|
|
}
|
|
}
|
|
}
|