bug fixes, daily reports, holidays

This commit is contained in:
Kwesi Banson Jnr
2026-09-08 08:01:08 +00:00
parent 26f23c825a
commit c96e49ccdf
23 changed files with 1148 additions and 160 deletions

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Http\Controllers;
use App\Models\DailyReport;
use App\Models\ReportOverride;
use App\Http\Requests\StoreDailyReportRequest;
use Illuminate\Support\Facades\Mail;
use App\Mail\DailyReportSubmitted;
class DailyReportController extends Controller
{
public function index()
{
$reports = auth()->user()->dailyReports()
->orderBy('report_date', 'desc')
->paginate(15);
return view('reports.index', compact('reports'));
}
public function store(StoreDailyReportRequest $request)
{
$report = DailyReport::create([
'user_id' => auth()->id(),
'report_date' => $request->report_date,
'content' => $request->content,
]);
// If they used an override, optionally delete or mark it as used so it can't be reused
ReportOverride::where('user_id', auth()->id())
->where('report_date', $request->report_date)
->delete();
$managementEmails = ['samuel@click-mobile.com'];
Mail::to($managementEmails)->send(new DailyReportSubmitted($report, auth()->user()));
return response()->json([
'success' => true,
'message' => 'Daily report submitted successfully.'
]);
}
}

View File

@@ -6,6 +6,8 @@ use App\Models\OfficeLocation;
use App\Models\StaffMember;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Models\PublicHoliday;
class OfficeLocationController extends Controller
{
@@ -114,5 +116,14 @@ class OfficeLocationController extends Controller
$document->delete();
return response()->json(['success' => true, 'message' => 'Document deleted.']);
}
}
}
}
// Swift track malawi
// eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI3MTYiLCJvaWQiOjcxNiwidWlkIjoiNDU3OTIzYzgtYmNkMS00ZWJhLWI3NzYtMGYyOTJmNmYzNWM4IiwiYXBpZCI6NTcxLCJpYXQiOjE3ODg1MzYwMzgsImV4cCI6MjEyODUzNjAzOH0.xbcNFzMNcryMACwYjOQMfF4Q06S80ttH4gYrcbduekTRXQcE4LptFEmir7iyeshghXy-C7Jcqno4UCKRIVnNig
// klick
// eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2OSIsIm9pZCI6NjksInVpZCI6IjEyMTdiMmE4LWNlYTYtNGE3ZC1hODlmLTFiODBhNDQxMzRjYSIsImFwaWQiOjQwMCwiaWF0IjoxNzI2NDc2MTc4LCJleHAiOjIwNjY0NzYxNzh9.7Yv2dyT3wEGz28ddFEMdEN0EsmD18hLSaHfNxIPCAskPjHFQ8819u5cLwfzNeZy5Xa3Xaz1ZNc66j0Fd8xSWDw

View File

@@ -30,6 +30,7 @@ class ProfileController extends Controller
'photo' => 'nullable|image|mimes:jpg,jpeg,png|max:2048',
]);
// Combine month and day if both are provided (e.g., "12-25")
if (!empty($request->birth_month) && !empty($request->birth_day)) {
$validated['dob'] = $request->birth_month . '-' . $request->birth_day;
} else {
@@ -52,8 +53,6 @@ class ProfileController extends Controller
return response()->json(['success' => true, 'message' => 'Profile updated successfully!']);
}
// --- DEPENDENTS / EMERGENCY CONTACTS CRUD ---
public function storeDependent(Request $request)
{
$staff = StaffMember::where('email', auth()->user()->email)->firstOrFail();

View File

@@ -0,0 +1,81 @@
<?php
namespace App\Http\Controllers;
use App\Models\OfficeLocation;
use App\Models\PublicHoliday;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
class PublicHolidaysController extends Controller
{
public function index($officeId)
{
$holidays = PublicHoliday::where('office_location_id', $officeId)
->orderBy('holiday_date', 'asc')
->get();
return response()->json($holidays);
}
public function store(Request $request)
{
$validated = $request->validate([
'office_location_id' => 'required|exists:office_locations,id',
'name' => 'required|string|max:255',
'holiday_date' => 'required|date',
'is_recurring' => 'boolean',
]);
$validated['is_recurring'] = $request->has('is_recurring');
PublicHoliday::create($validated);
return response()->json(['success' => true]);
}
public function destroy(PublicHoliday $holiday)
{
$holiday->delete();
return response()->json(['success' => true]);
}
public function importFromApi(Request $request, $officeId)
{
$location = OfficeLocation::findOrFail($officeId);
$year = $request->input('year', now()->year);
$countryCode = $request->input('country_code');
if (!$countryCode) {
return response()->json(['success' => false, 'message' => 'Country code missing.'], 400);
}
$response = Http::get("https://date.nager.at/api/v3/PublicHolidays/{$year}/{$countryCode}");
if ($response->successful()) {
$apiHolidays = $response->json();
$count = 0;
foreach ($apiHolidays as $holiday) {
PublicHoliday::updateOrCreate(
[
'office_location_id' => $location->id,
'holiday_date' => $holiday['date'],
],
[
'name' => $holiday['name'],
'is_recurring' => $holiday['fixed'] ?? false,
]
);
$count++;
}
return response()->json([
'success' => true,
'message' => "Successfully imported {$count} holidays."
]);
}
return response()->json(['success' => false, 'message' => 'Failed to fetch from API.'], 500);
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers;
use App\Models\ReportOverride;
use Illuminate\Http\Request;
use Carbon\Carbon;
class ReportOverrideController extends Controller
{
public function store(Request $request)
{
// Todo : Add middleware or policy checks for management
$request->validate([
'user_id' => 'required|exists:staff_members,id',
'report_date' => 'required|date',
'hours_valid' => 'nullable|integer|min:1|max:72',
]);
$hoursValid = (int) ($request->input('hours_valid') ?? 24);
ReportOverride::updateOrCreate(
[
'user_id' => $request->user_id,
'report_date' => $request->report_date,
],
[
'granted_by' => auth()->id(),
'expires_at' => now()->addHours($hoursValid),
// 'expires_at' => now()->addHours($request->hours_valid ?? 24),
]
);
return response()->json([
'success' => true,
'message' => 'Late submission override granted successfully.'
]);
}
}

View File

@@ -17,7 +17,6 @@ class StaffMembersController extends Controller
{
$query = StaffMember::query();
// 1. Search Filter
if ($request->filled('search')) {
$search = $request->search;
$query->where(function($q) use ($search) {
@@ -28,12 +27,11 @@ class StaffMembersController extends Controller
});
}
// 2. Department Filter
if ($request->filled('department_id')) {
$query->where('department_id', $request->department_id);
}
// 3. Status Filter
if ($request->filled('status')) {
$query->where('status', $request->status);
}
@@ -41,15 +39,19 @@ class StaffMembersController extends Controller
$staffMembers = $query->orderBy('name', 'asc')->paginate(12);
$staffMembers->appends($request->query());
// Fetch dynamic dropdown lists
$departments = Department::orderBy('name', 'asc')->get();
$countries = Country::orderBy('en_short_name', 'asc')->get();
$allstaffMembers = StaffMember::orderBy('name')->get(['id', 'name']);
// dd($staffMembers);
// dd(\Auth::user()->designation);
$data = [
'staffMembers' => $staffMembers,
'departments' => $departments,
'allstaffMembers' => $allstaffMembers,
'countries' => $countries,
];
return view('staff.index', $data);
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Carbon\Carbon;
use App\Models\ReportOverride;
class StoreDailyReportRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
public function rules(): array
{
return [
'report_date' => 'required|date|before_or_equal:today',
'content' => 'required|string',
];
}
public function withValidator($validator)
{
$validator->after(function ($validator) {
$reportDate = Carbon::parse($this->report_date)->startOfDay();
// Calculate standard deadline: 48 hours from the end of the report date
$deadline = $reportDate->copy()->endOfDay()->addHours(48);
if (now()->greaterThan($deadline)) {
// Check if a valid override exists
$hasOverride = ReportOverride::where('user_id', auth()->id())
->where('report_date', $this->report_date)
->where('expires_at', '>', now())
->exists();
if (!$hasOverride) {
$validator->errors()->add(
'report_date',
'The 48-hour submission window for this date has expired. Please contact management for an override.'
);
}
}
});
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class DailyReportSubmitted extends Mailable
{
use Queueable, SerializesModels;
public $report;
public $user;
public function __construct($report, $user)
{
$this->report = $report;
$this->user = $user;
}
public function build()
{
return $this->subject('New Daily Report: ' . $this->user->name . ' - ' . $this->report->report_date->format('d M Y'))
->view('emails.reports.submitted');
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class DailyReport extends Model
{
protected $fillable = ['user_id', 'report_date', 'content'];
protected $casts = [
'report_date' => 'date',
];
public function user()
{
return $this->belongsTo(StaffMember::class);
}
}

View File

@@ -29,4 +29,21 @@ class OfficeLocation extends Model
{
return $this->hasMany(OfficeLocationDocument::class, 'office_location_id');
}
public function holidays()
{
return $this->hasMany(PublicHoliday::class)->orderBy('holiday_date');
}
public function isHoliday($date)
{
$parsedDate = \Carbon\Carbon::parse($date);
return $this->holidays()->where(function($query) use ($parsedDate) {
$query->whereDate('holiday_date', $parsedDate->format('Y-m-d'))
->orWhere(function($q) use ($parsedDate) {
$q->where('is_recurring', true)
->whereMonth('holiday_date', $parsedDate->month)
->whereDay('holiday_date', $parsedDate->day);
});
})->exists();
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class PublicHoliday extends Model
{
protected $fillable = ['office_location_id', 'name', 'holiday_date', 'is_recurring'];
// protected $guarded = ['id']
protected $casts = [
'holiday_date' => 'date',
'is_recurring' => 'boolean',
];
public function location()
{
return $this->belongsTo(OfficeLocation::class, 'office_location_id');
}
}

View File

@@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ReportOverride extends Model
{
protected $guarded = ['id'];
}

View File

@@ -27,7 +27,13 @@ class StaffMember extends Authenticatable
public function dependents() {
return $this->hasMany(StaffDependent::class, 'staff_id');
}
public function dailyReports() {
return $this->hasMany(DailyReport::class, 'user_id');
}
public function department(){
return $this->hasOne('App\Models\Department', 'id', 'department_id');
}
// 4. Cast dates correctly
protected $casts = [
// 'dob' => 'date',