632 lines
24 KiB
PHP
632 lines
24 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Announcement;
|
|
use App\Models\AnnouncementView;
|
|
use App\Models\Branch;
|
|
use App\Models\Department;
|
|
use App\Models\User;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Inertia\Inertia;
|
|
|
|
class AnnouncementController extends Controller
|
|
{
|
|
/**
|
|
* Display a listing of the resource.
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
if (Auth::user()->can('manage-announcements')) {
|
|
$query = Announcement::with(['departments', 'branches'])->where(function ($q) {
|
|
if (Auth::user()->can('manage-any-announcements')) {
|
|
$q->whereIn('created_by', getCompanyAndUsersId());
|
|
} elseif (Auth::user()->can('manage-own-announcements')) {
|
|
$q->where('created_by', Auth::id());
|
|
} else {
|
|
$q->whereRaw('1 = 0');
|
|
}
|
|
});
|
|
|
|
// Handle search
|
|
if ($request->has('search') && !empty($request->search)) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('title', 'like', '%' . $request->search . '%')
|
|
->orWhere('description', 'like', '%' . $request->search . '%')
|
|
->orWhere('content', 'like', '%' . $request->search . '%');
|
|
});
|
|
}
|
|
|
|
// Handle category filter
|
|
if ($request->has('category') && !empty($request->category)) {
|
|
$query->where('category', $request->category);
|
|
}
|
|
|
|
// Handle department filter
|
|
if ($request->has('department_id') && !empty($request->department_id)) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('is_company_wide', true)
|
|
->orWhereHas('departments', function ($q) use ($request) {
|
|
$q->where('departments.id', $request->department_id);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle branch filter
|
|
if ($request->has('branch_id') && !empty($request->branch_id)) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('is_company_wide', true)
|
|
->orWhereHas('branches', function ($q) use ($request) {
|
|
$q->where('branches.id', $request->branch_id);
|
|
});
|
|
});
|
|
}
|
|
|
|
// Handle status filter
|
|
if ($request->has('status') && !empty($request->status)) {
|
|
$today = now()->format('Y-m-d');
|
|
|
|
if ($request->status === 'active') {
|
|
$query->where('start_date', '<=', $today)
|
|
->where(function ($q) use ($today) {
|
|
$q->whereNull('end_date')
|
|
->orWhere('end_date', '>=', $today);
|
|
});
|
|
} elseif ($request->status === 'upcoming') {
|
|
$query->where('start_date', '>', $today);
|
|
} elseif ($request->status === 'expired') {
|
|
$query->whereNotNull('end_date')
|
|
->where('end_date', '<', $today);
|
|
}
|
|
}
|
|
|
|
// Handle priority filter
|
|
if ($request->has('priority') && !empty($request->priority)) {
|
|
if ($request->priority === 'high') {
|
|
$query->where('is_high_priority', true);
|
|
} elseif ($request->priority === 'normal') {
|
|
$query->where('is_high_priority', false);
|
|
}
|
|
}
|
|
|
|
// Handle featured filter
|
|
if ($request->has('featured') && $request->featured === 'true') {
|
|
$query->where('is_featured', true);
|
|
}
|
|
|
|
// Handle date range filter
|
|
if ($request->has('date_from') && !empty($request->date_from)) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('start_date', '>=', $request->date_from)
|
|
->orWhere('end_date', '>=', $request->date_from);
|
|
});
|
|
}
|
|
if ($request->has('date_to') && !empty($request->date_to)) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('start_date', '<=', $request->date_to)
|
|
->orWhere('end_date', '<=', $request->date_to);
|
|
});
|
|
}
|
|
|
|
// Handle sorting
|
|
$allowedSortFields = ['id', 'title', 'category', 'start_date', 'end_date', 'is_featured', 'is_high_priority', 'created_at'];
|
|
if ($request->has('sort_field') && !empty($request->sort_field)) {
|
|
$sortField = $request->sort_field === 'date_range' ? 'start_date' : $request->sort_field;
|
|
if (in_array($sortField, $allowedSortFields)) {
|
|
$sortDirection = in_array($request->sort_direction, ['asc', 'desc']) ? $request->sort_direction : 'asc';
|
|
$query->orderBy($sortField, $sortDirection);
|
|
} else {
|
|
$query->orderBy('created_at', 'desc');
|
|
}
|
|
} else {
|
|
$query->orderBy('created_at', 'desc');
|
|
}
|
|
|
|
$announcements = $query->paginate($request->per_page ?? 10);
|
|
|
|
// Get departments for filter dropdown
|
|
$departments = Department::whereIn('created_by', getCompanyAndUsersId())
|
|
->select('id', 'name')
|
|
->get();
|
|
|
|
// Get branches for filter dropdown
|
|
$branches = Branch::whereIn('created_by', getCompanyAndUsersId())
|
|
->select('id', 'name')
|
|
->get();
|
|
|
|
// Get categories for filter dropdown
|
|
$categories = Announcement::whereIn('created_by', getCompanyAndUsersId())
|
|
->select('category')
|
|
->distinct()
|
|
->pluck('category')
|
|
->toArray();
|
|
|
|
return Inertia::render('hr/announcements/index', [
|
|
'announcements' => $announcements,
|
|
'departments' => $departments,
|
|
'branches' => $branches,
|
|
'categories' => $categories,
|
|
'filters' => $request->all(['search', 'category', 'department_id', 'branch_id', 'status', 'priority', 'featured', 'date_from', 'date_to', 'sort_field', 'sort_direction', 'per_page']),
|
|
]);
|
|
} else {
|
|
return redirect()->back()->with('error', __('Permission Denied.'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Display the dashboard view.
|
|
*/
|
|
public function dashboard(Request $request)
|
|
{
|
|
$today = now()->format('Y-m-d');
|
|
|
|
// Get all announcements (active, expired, upcoming)
|
|
$allAnnouncements = Announcement::with(['departments', 'branches'])
|
|
->whereIn('created_by', getCompanyAndUsersId())
|
|
->orderBy('is_high_priority', 'desc')
|
|
->orderBy('is_featured', 'desc')
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
// Get featured announcements (from all announcements)
|
|
$featuredAnnouncements = Announcement::with(['departments', 'branches'])
|
|
->whereIn('created_by', getCompanyAndUsersId())
|
|
->where('is_featured', true)
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
// Get high priority announcements (from all announcements)
|
|
$highPriorityAnnouncements = Announcement::with(['departments', 'branches'])
|
|
->whereIn('created_by', getCompanyAndUsersId())
|
|
->where('is_high_priority', true)
|
|
->orderBy('created_at', 'desc')
|
|
->get();
|
|
|
|
// Get upcoming announcements
|
|
$upcomingAnnouncements = Announcement::with(['departments', 'branches'])
|
|
->whereIn('created_by', getCompanyAndUsersId())
|
|
->where('start_date', '>', $today)
|
|
->orderBy('start_date', 'asc')
|
|
->take(5)
|
|
->get();
|
|
|
|
// Get categories for filter
|
|
$categories = Announcement::whereIn('created_by', getCompanyAndUsersId())
|
|
->select('category')
|
|
->distinct()
|
|
->pluck('category')
|
|
->toArray();
|
|
|
|
// Get departments for filter
|
|
$departments = Department::whereIn('created_by', getCompanyAndUsersId())
|
|
->select('id', 'name')
|
|
->get();
|
|
|
|
// Get branches for filter
|
|
$branches = Branch::whereIn('created_by', getCompanyAndUsersId())
|
|
->select('id', 'name')
|
|
->get();
|
|
|
|
// Get employee for marking announcements as read
|
|
$employee = null;
|
|
if (Auth::user()->type !== 'company' && Auth::user()->type !== 'superadmin') {
|
|
$employee = User::where('id', Auth::id())->first();
|
|
}
|
|
|
|
return Inertia::render('hr/announcements/dashboard', [
|
|
'allAnnouncements' => $allAnnouncements,
|
|
'featuredAnnouncements' => $featuredAnnouncements,
|
|
'highPriorityAnnouncements' => $highPriorityAnnouncements,
|
|
'upcomingAnnouncements' => $upcomingAnnouncements,
|
|
'categories' => $categories,
|
|
'departments' => $departments,
|
|
'branches' => $branches,
|
|
'employee' => $employee,
|
|
'filters' => $request->all(['category', 'department_id', 'branch_id']),
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Store a newly created resource in storage.
|
|
*/
|
|
public function store(Request $request)
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'title' => 'required|string|max:255',
|
|
'category' => 'required|string|max:255',
|
|
'description' => 'nullable|string',
|
|
'content' => 'required|string',
|
|
'start_date' => 'required|date',
|
|
'end_date' => 'nullable|date|after_or_equal:start_date',
|
|
'attachments' => 'nullable|string',
|
|
'is_featured' => 'nullable|boolean',
|
|
'is_high_priority' => 'nullable|boolean',
|
|
'is_company_wide' => 'nullable|boolean',
|
|
'department_ids' => 'nullable|string|required_if:is_company_wide,false',
|
|
'branch_ids' => 'nullable|string|required_if:is_company_wide,false',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return redirect()->back()->withErrors($validator)->withInput();
|
|
}
|
|
|
|
// Check if departments and branches belong to current company
|
|
if (
|
|
!$request->is_company_wide &&
|
|
(empty($request->department_ids) && empty($request->branch_ids))
|
|
) {
|
|
return redirect()->back()->with('error', 'You must select at least one department or branch if the announcement is not company-wide');
|
|
}
|
|
|
|
if (!empty($request->department_ids)) {
|
|
$validDepartment = Department::where('created_by', createdBy())
|
|
->where('id', $request->department_ids)
|
|
->exists();
|
|
|
|
if (!$validDepartment) {
|
|
return redirect()->back()->with('error', 'Invalid department selection');
|
|
}
|
|
}
|
|
|
|
if (!empty($request->branch_ids)) {
|
|
$validBranch = Branch::where('created_by', createdBy())
|
|
->where('id', $request->branch_ids)
|
|
->exists();
|
|
|
|
if (!$validBranch) {
|
|
return redirect()->back()->with('error', 'Invalid branch selection');
|
|
}
|
|
}
|
|
|
|
$announcementData = [
|
|
'title' => $request->title,
|
|
'category' => $request->category,
|
|
'description' => $request->description,
|
|
'content' => $request->content,
|
|
'start_date' => $request->start_date,
|
|
'end_date' => $request->end_date,
|
|
'is_featured' => $request->is_featured ?? false,
|
|
'is_high_priority' => $request->is_high_priority ?? false,
|
|
'is_company_wide' => $request->is_company_wide ?? true,
|
|
'created_by' => creatorId(),
|
|
];
|
|
|
|
// Handle attachment from media library
|
|
if ($request->has('attachments')) {
|
|
$announcementData['attachments'] = $request->attachments;
|
|
}
|
|
|
|
$announcement = Announcement::create($announcementData);
|
|
|
|
// Attach departments and branches if not company-wide
|
|
if (!$request->is_company_wide) {
|
|
if (!empty($request->department_ids)) {
|
|
$announcement->departments()->attach([$request->department_ids]);
|
|
}
|
|
|
|
if (!empty($request->branch_ids)) {
|
|
$announcement->branches()->attach([$request->branch_ids]);
|
|
}
|
|
}
|
|
|
|
return redirect()->back()->with('success', __('Announcement created successfully'));
|
|
}
|
|
|
|
/**
|
|
* Display the specified resource.
|
|
*/
|
|
public function show(Announcement $announcement)
|
|
{
|
|
// Check if announcement belongs to current company
|
|
if (!in_array($announcement->created_by, getCompanyAndUsersId())) {
|
|
return redirect()->back()->with('error', __('You do not have permission to view this announcement'));
|
|
}
|
|
|
|
// Load relationships
|
|
$announcement->load(['departments', 'branches']);
|
|
|
|
// Get view statistics
|
|
$viewCount = $announcement->viewedBy()->count();
|
|
$totalEmployees = User::where('type', 'employee')
|
|
->whereIn('created_by', getCompanyAndUsersId())
|
|
->count();
|
|
$viewPercentage = $totalEmployees > 0 ? round(($viewCount / $totalEmployees) * 100) : 0;
|
|
|
|
// Mark as viewed if current user is an employee
|
|
if (Auth::user()->type !== 'company' && Auth::user()->type !== 'superadmin') {
|
|
$employee = User::where('id', Auth::id())->first();
|
|
|
|
if ($employee) {
|
|
// Check if already viewed
|
|
$existingView = AnnouncementView::where('announcement_id', $announcement->id)
|
|
->where('employee_id', $employee->id)
|
|
->first();
|
|
|
|
if (!$existingView) {
|
|
// Mark as viewed
|
|
$announcement->viewedBy()->attach($employee->id, [
|
|
'viewed_at' => now()
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Inertia::render('hr/announcements/show', [
|
|
'announcement' => $announcement,
|
|
'viewCount' => $viewCount,
|
|
'totalEmployees' => $totalEmployees,
|
|
'viewPercentage' => $viewPercentage,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Update the specified resource in storage.
|
|
*/
|
|
public function update(Request $request, Announcement $announcement)
|
|
{
|
|
// Check if announcement belongs to current company
|
|
if (!in_array($announcement->created_by, getCompanyAndUsersId())) {
|
|
return redirect()->back()->with('error', 'You do not have permission to update this announcement');
|
|
}
|
|
|
|
$validator = Validator::make($request->all(), [
|
|
'title' => 'required|string|max:255',
|
|
'category' => 'required|string|max:255',
|
|
'description' => 'nullable|string',
|
|
'content' => 'required|string',
|
|
'start_date' => 'required|date',
|
|
'end_date' => 'nullable|date|after_or_equal:start_date',
|
|
'attachments' => 'nullable|string',
|
|
'is_featured' => 'nullable|boolean',
|
|
'is_high_priority' => 'nullable|boolean',
|
|
'is_company_wide' => 'nullable|boolean',
|
|
'department_ids' => 'nullable|string|required_if:is_company_wide,false',
|
|
'branch_ids' => 'nullable|string|required_if:is_company_wide,false',
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return redirect()->back()->withErrors($validator)->withInput();
|
|
}
|
|
|
|
// Check if departments and branches belong to current company
|
|
if (
|
|
!$request->is_company_wide &&
|
|
(empty($request->department_ids) && empty($request->branch_ids))
|
|
) {
|
|
return redirect()->back()->with('error', 'You must select at least one department or branch if the announcement is not company-wide');
|
|
}
|
|
|
|
if (!empty($request->department_ids)) {
|
|
$departmentId = $request->department_ids;
|
|
$validDepartment = Department::where('created_by', createdBy())
|
|
->where('id', $departmentId)
|
|
->exists();
|
|
|
|
if (!$validDepartment) {
|
|
return redirect()->back()->with('error', 'Invalid department selection');
|
|
}
|
|
}
|
|
|
|
if (!empty($request->branch_ids)) {
|
|
$branchId = $request->branch_ids;
|
|
$validBranch = Branch::where('created_by', createdBy())
|
|
->where('id', $branchId)
|
|
->exists();
|
|
|
|
if (!$validBranch) {
|
|
return redirect()->back()->with('error', 'Invalid branch selection');
|
|
}
|
|
}
|
|
|
|
$announcementData = [
|
|
'title' => $request->title,
|
|
'category' => $request->category,
|
|
'description' => $request->description,
|
|
'content' => $request->content,
|
|
'start_date' => $request->start_date,
|
|
'end_date' => $request->end_date,
|
|
'is_featured' => $request->is_featured ?? false,
|
|
'is_high_priority' => $request->is_high_priority ?? false,
|
|
'is_company_wide' => $request->is_company_wide ?? true,
|
|
];
|
|
|
|
// Handle attachment from media library
|
|
if ($request->has('attachments')) {
|
|
$announcementData['attachments'] = $request->attachments;
|
|
}
|
|
|
|
$announcement->update($announcementData);
|
|
|
|
// Sync departments and branches
|
|
if ($request->is_company_wide) {
|
|
$announcement->departments()->detach();
|
|
$announcement->branches()->detach();
|
|
} else {
|
|
if (!empty($request->department_ids)) {
|
|
$announcement->departments()->sync([$request->department_ids]);
|
|
} else {
|
|
$announcement->departments()->detach();
|
|
}
|
|
|
|
if (!empty($request->branch_ids)) {
|
|
$announcement->branches()->sync([$request->branch_ids]);
|
|
} else {
|
|
$announcement->branches()->detach();
|
|
}
|
|
}
|
|
|
|
return redirect()->back()->with('success', __('Announcement updated successfully'));
|
|
}
|
|
|
|
/**
|
|
* Remove the specified resource from storage.
|
|
*/
|
|
public function destroy(Announcement $announcement)
|
|
{
|
|
// Check if announcement belongs to current company
|
|
if (!in_array($announcement->created_by, getCompanyAndUsersId())) {
|
|
return redirect()->back()->with('error', 'You do not have permission to delete this announcement');
|
|
}
|
|
|
|
// Detach all departments and branches
|
|
$announcement->departments()->detach();
|
|
$announcement->branches()->detach();
|
|
|
|
// Delete all views
|
|
$announcement->viewedBy()->detach();
|
|
|
|
// Delete the announcement
|
|
$announcement->delete();
|
|
|
|
return redirect()->back()->with('success', __('Announcement deleted successfully'));
|
|
}
|
|
|
|
/**
|
|
* Download attachment file.
|
|
*/
|
|
public function downloadAttachment(Announcement $announcement)
|
|
{
|
|
// Check if announcement belongs to current company
|
|
if (!in_array($announcement->created_by, getCompanyAndUsersId())) {
|
|
return redirect()->back()->with('error', __('You do not have permission to access this attachment'));
|
|
}
|
|
|
|
if (!$announcement->attachments) {
|
|
return redirect()->back()->with('error', __('Attachment file not found'));
|
|
}
|
|
|
|
$filePath = getStorageFilePath($announcement->attachments);
|
|
|
|
if (!file_exists($filePath)) {
|
|
return redirect()->back()->with('error', __('Attachment file not found'));
|
|
}
|
|
|
|
return response()->download($filePath);
|
|
}
|
|
|
|
/**
|
|
* Mark announcement as read for current employee.
|
|
*/
|
|
public function markAsRead(Request $request, Announcement $announcement)
|
|
{
|
|
$employee = User::where('id', Auth::id())->first();
|
|
|
|
if (!$employee) {
|
|
return response()->json(['error' => 'Employee not found'], 404);
|
|
}
|
|
|
|
// Check if already viewed
|
|
$existingView = AnnouncementView::where('announcement_id', $announcement->id)
|
|
->where('employee_id', $employee->id)
|
|
->first();
|
|
|
|
if (!$existingView) {
|
|
// Mark as viewed
|
|
$announcement->viewedBy()->attach($employee->id, [
|
|
'viewed_at' => now()
|
|
]);
|
|
}
|
|
|
|
return response()->json(['success' => true]);
|
|
}
|
|
|
|
/**
|
|
* Get announcement view statistics.
|
|
*/
|
|
public function viewStatistics(Announcement $announcement)
|
|
{
|
|
// Check if announcement belongs to current company
|
|
if (!in_array($announcement->created_by, getCompanyAndUsersId())) {
|
|
return redirect()->back()->with('error', __('You do not have permission to view these statistics'));
|
|
}
|
|
|
|
// Load viewed by (employees)
|
|
$views = $announcement->viewedBy()->get();
|
|
|
|
// Get total employees
|
|
$totalEmployees = User::where('type', 'employee')->whereIn('created_by', getCompanyAndUsersId())->count();
|
|
|
|
// Get statistics only for announcement's target branch and department
|
|
$departmentStats = [];
|
|
$branchStats = [];
|
|
|
|
if (!$announcement->is_company_wide) {
|
|
// Get target branch and department
|
|
$targetBranch = $announcement->branches->first();
|
|
$targetDepartment = $announcement->departments->first();
|
|
|
|
if ($targetBranch) {
|
|
$branchEmployees = User::where('type', 'employee')->whereIn('created_by', getCompanyAndUsersId())->whereHas('employee', function ($q) use ($targetBranch) {
|
|
$q->where('branch_id', $targetBranch->id);
|
|
})->count();
|
|
$branchViews = $announcement->viewedBy()
|
|
->whereHas('employee', function ($q) use ($targetBranch) {
|
|
$q->where('branch_id', $targetBranch->id);
|
|
})
|
|
->count();
|
|
|
|
$branchStats[] = [
|
|
'branch' => $targetBranch->name,
|
|
'total' => $branchEmployees,
|
|
'viewed' => $branchViews,
|
|
'percentage' => $branchEmployees > 0 ? round(($branchViews / $branchEmployees) * 100) : 0
|
|
];
|
|
}
|
|
|
|
if ($targetDepartment) {
|
|
$departmentEmployees = User::where('type', 'employee')->whereIn('created_by', getCompanyAndUsersId())->whereHas('employee', function ($q) use ($targetDepartment) {
|
|
$q->where('department_id', $targetDepartment->id);
|
|
})->count();
|
|
|
|
$departmentViews = $announcement->viewedBy()
|
|
->whereHas('employee', function ($q) use ($targetDepartment) {
|
|
$q->where('department_id', $targetDepartment->id);
|
|
})
|
|
->count();
|
|
|
|
$departmentStats[] = [
|
|
'branch_name' => $targetBranch ? $targetBranch->name : 'Unknown',
|
|
'departments' => [[
|
|
'department' => $targetDepartment->name,
|
|
'total' => $departmentEmployees,
|
|
'viewed' => $departmentViews,
|
|
'percentage' => $departmentEmployees > 0 ? round(($departmentViews / $departmentEmployees) * 100) : 0
|
|
]]
|
|
];
|
|
}
|
|
}
|
|
|
|
return Inertia::render('hr/announcements/statistics', [
|
|
'announcement' => $announcement,
|
|
'totalEmployees' => $totalEmployees,
|
|
'viewedCount' => $views->count(),
|
|
'viewPercentage' => $totalEmployees > 0 ? round(($views->count() / $totalEmployees) * 100) : 0,
|
|
'departmentStats' => $departmentStats,
|
|
'branchStats' => $branchStats,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get departments based on selected branches.
|
|
*/
|
|
public function getDepartments($branchIds)
|
|
{
|
|
$branchIdArray = explode(',', $branchIds);
|
|
|
|
$departments = Department::whereIn('branch_id', $branchIdArray)
|
|
->whereIn('created_by', getCompanyAndUsersId())
|
|
->select('id', 'name')
|
|
->get()
|
|
->map(function ($dept) {
|
|
return [
|
|
'value' => $dept->id,
|
|
'label' => $dept->name
|
|
];
|
|
});
|
|
return response()->json($departments);
|
|
}
|
|
}
|