From a2ea56efa90a38a8119994b97292f9fdcba3ed1d Mon Sep 17 00:00:00 2001 From: Ajjj Date: Tue, 4 Aug 2026 11:50:16 +0800 Subject: [PATCH] feat: implement executive dashboard with financial analytics, blocker tracking, and resolution management systems --- .../Controllers/DailyReportsController.php | 23 + .../app/Models/DailyReportIssue.php | 11 + ...on_fields_to_daily_report_issues_table.php | 35 ++ .../DailyReports/resources/js/Pages/Show.tsx | 509 +++++++++++++----- Modules/DailyReports/routes/web.php | 2 + app/Http/Controllers/DashboardController.php | 28 +- .../js/Components/Dashboard/BlockerList.tsx | 140 +++-- .../Dashboard/ExecutiveDashboardView.tsx | 239 +++++--- tests/Feature/ComprehensiveSystemE2ETest.php | 249 +++++++++ 9 files changed, 967 insertions(+), 269 deletions(-) create mode 100644 Modules/DailyReports/database/migrations/2026_08_04_000001_add_resolution_fields_to_daily_report_issues_table.php create mode 100644 tests/Feature/ComprehensiveSystemE2ETest.php diff --git a/Modules/DailyReports/app/Http/Controllers/DailyReportsController.php b/Modules/DailyReports/app/Http/Controllers/DailyReportsController.php index f39b1f4..ca6dab0 100644 --- a/Modules/DailyReports/app/Http/Controllers/DailyReportsController.php +++ b/Modules/DailyReports/app/Http/Controllers/DailyReportsController.php @@ -214,6 +214,29 @@ class DailyReportsController extends Controller ); } + /** + * Resolve a daily report issue with supervisor compliance details. + */ + public function resolveIssue(Request $request, DailyReport $daily_report, \Modules\DailyReports\Models\DailyReportIssue $issue) + { + $validated = $request->validate([ + 'supervisor_name' => 'required|string|max:255', + 'supervisor_role' => 'required|string|max:255', + 'resolution_notes' => 'required|string', + ]); + + $issue->update([ + 'status' => 'resolved', + 'supervisor_name' => $validated['supervisor_name'], + 'supervisor_role' => $validated['supervisor_role'], + 'resolution_notes' => $validated['resolution_notes'], + 'resolved_at' => now(), + 'resolved_by_user_id' => auth()->id(), + ]); + + return redirect()->back()->with('success', 'Issue marked as resolved with compliance record.'); + } + /** * Remove the specified resource from storage. */ diff --git a/Modules/DailyReports/app/Models/DailyReportIssue.php b/Modules/DailyReports/app/Models/DailyReportIssue.php index cf69dc6..4eb4aaa 100644 --- a/Modules/DailyReports/app/Models/DailyReportIssue.php +++ b/Modules/DailyReports/app/Models/DailyReportIssue.php @@ -12,8 +12,19 @@ class DailyReportIssue extends Model 'issue_type', 'description', 'delay_impact', + 'status', + 'supervisor_name', + 'supervisor_role', + 'resolution_notes', + 'resolved_at', + 'resolved_by_user_id', ]; + public function resolvedBy(): BelongsTo + { + return $this->belongsTo(\App\Models\User::class, 'resolved_by_user_id'); + } + public function dailyReport(): BelongsTo { return $this->belongsTo(DailyReport::class); diff --git a/Modules/DailyReports/database/migrations/2026_08_04_000001_add_resolution_fields_to_daily_report_issues_table.php b/Modules/DailyReports/database/migrations/2026_08_04_000001_add_resolution_fields_to_daily_report_issues_table.php new file mode 100644 index 0000000..e93c64e --- /dev/null +++ b/Modules/DailyReports/database/migrations/2026_08_04_000001_add_resolution_fields_to_daily_report_issues_table.php @@ -0,0 +1,35 @@ +string('status')->default('open'); // open, resolved + $table->string('supervisor_name')->nullable(); + $table->string('supervisor_role')->nullable(); + $table->text('resolution_notes')->nullable(); + $table->timestamp('resolved_at')->nullable(); + $table->foreignId('resolved_by_user_id')->nullable()->constrained('users')->nullOnDelete(); + }); + } + + public function down(): void + { + Schema::table('daily_report_issues', function (Blueprint $table) { + $table->dropForeign(['resolved_by_user_id']); + $table->dropColumn([ + 'status', + 'supervisor_name', + 'supervisor_role', + 'resolution_notes', + 'resolved_at', + 'resolved_by_user_id', + ]); + }); + } +}; diff --git a/Modules/DailyReports/resources/js/Pages/Show.tsx b/Modules/DailyReports/resources/js/Pages/Show.tsx index 7c9e18d..cfda0f3 100644 --- a/Modules/DailyReports/resources/js/Pages/Show.tsx +++ b/Modules/DailyReports/resources/js/Pages/Show.tsx @@ -1,124 +1,281 @@ -import React from 'react'; -import { Head, Link } from '@inertiajs/react'; +import React, { useState } from 'react'; +import { Head, Link, useForm } from '@inertiajs/react'; import ProjectLayout from '../../../../ProjectManagement/resources/js/Layouts/ProjectLayout'; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/Components/ui/card'; +import { Card, CardContent, CardHeader, CardTitle } from '@/Components/ui/card'; import { Button } from '@/Components/ui/button'; -import { Pencil, Calendar, Clock, Cloud, User, AlertCircle, Download } from 'lucide-react'; +import { Input } from '@/Components/ui/input'; +import { Label } from '@/Components/ui/label'; +import { Textarea } from '@/Components/ui/textarea'; +import { + Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle +} from "@/Components/ui/dialog"; +import { + Pencil, Calendar, Clock, Cloud, User, AlertTriangle, + Download, ShieldAlert, CheckCircle2, HardHat, Wrench, + Package, MessageSquare, ArrowLeft, Sparkles, AlertCircle, ShieldCheck +} from 'lucide-react'; import { Badge } from '@/Components/ui/badge'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/Components/ui/table"; export default function Show({ report }: any) { const project = report.project; + const [selectedIssue, setSelectedIssue] = useState(null); + const [isResolveModalOpen, setIsResolveModalOpen] = useState(false); + + const { data, setData, post, processing, errors, reset } = useForm({ + supervisor_name: '', + supervisor_role: 'Site Supervisor', + resolution_notes: '', + }); + + const openResolveModal = (issue: any) => { + setSelectedIssue(issue); + setData({ + supervisor_name: report.user?.name || '', + supervisor_role: 'Construction Supervisor', + resolution_notes: '', + }); + setIsResolveModalOpen(true); + }; + + const handleResolveSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (!selectedIssue) return; + + post(route('projects.daily-reports.issues.resolve', { daily_report: report.id, issue: selectedIssue.id }), { + onSuccess: () => { + setIsResolveModalOpen(false); + setSelectedIssue(null); + reset(); + }, + }); + }; return ( -
-
-

Daily Report #{report.report_number || report.id}

-

Submitted by {report.user?.name} on {report.created_at ? new Date(report.created_at).toLocaleDateString() : ''}

+ {/* Action Bar Header */} +
+
+ + + +
+
+

+ Daily Report #{report.report_number || report.id} +

+ + Published + +
+

+ + {report.user?.name} + + + Submitted {report.created_at ? new Date(report.created_at).toLocaleDateString(undefined, { dateStyle: 'medium' }) : ''} +

+
-
- {/* Printable Area Starts */} + {/* Printable Container */}
-
-

Daily Construction Report

-
+ + {/* Print Only Title Header */} +
+

GSB Construction — Daily Operations Report

+
Project: {project?.name}
Date: {report.report_date}
Report #: {report.report_number || report.id}
-
- - - Project & Date - - -
- Project Name: - {project?.name} -
-
- Report Date: - {report.report_date} -
-
- Working Hours: - {report.start_time || '--'} to {report.end_time || '--'} -
-
- Prepared By: - {report.user?.name} -
-
-
+ {/* Summary Row */} +
+ + {/* Left: Project & Timeline Overview */} +
+ + + + Project & Date Overview + + + +
+ Project Name + {project?.name} +
+
+ Report Date + + {report.report_date} + +
+
+ Working Hours + + {report.start_time || '--'} to {report.end_time || '--'} + +
+
+ Prepared By + + {report.user?.name} + +
+
+
+
+ + {/* Right: Weather Conditions */} +
+ + + + Weather Conditions + + + +
+ Weather + {report.weather || 'N/A'} +
+
+ Temperature + {report.temperature || 'N/A'} +
+
+ Precipitation + {report.precipitation || 'N/A'} +
+
+ Wind + {report.wind || 'N/A'} +
+
+
+
- - - Weather Conditions - - -
- Weather: - {report.weather || 'N/A'} -
-
- Temperature: - {report.temperature || 'N/A'} -
-
- Precipitation: - {report.precipitation || 'N/A'} -
-
- Wind: - {report.wind || 'N/A'} -
-
-
- {report.activities && report.activities.length > 0 && ( - - - Tasks + {/* Issues & Concern Section */} + {report.issues && report.issues.length > 0 && ( + + + + Issues & Concern ({report.issues.length}) + - + - Activity/Task - Zone/Area - Quantity - % Complete + Status + Type + Description + Delay Impact + Action + + + + {report.issues.map((issue: any, i: number) => { + const isResolved = issue.status === 'resolved'; + return ( + + + {isResolved ? ( + + Resolved + + ) : ( + + Open Issue + + )} + + + {issue.issue_type} + + +
{issue.description}
+ {isResolved && ( +
+ + Supervised by {issue.supervisor_name} ({issue.supervisor_role}) +
+ )} +
+ {issue.delay_impact || '-'} + + {isResolved ? ( + + Resolved on {issue.resolved_at ? new Date(issue.resolved_at).toLocaleDateString() : ''} + + ) : ( + + )} + +
+ ); + })} +
+
+
+
+ )} + + {/* Tasks Section */} + {report.activities && report.activities.length > 0 && ( + + + Tasks + + + + + + Activity/Task + Zone/Area + Quantity + % Complete {report.activities.map((a: any, i: number) => ( - {a.task_name} - {a.zone_area || '-'} - {a.quantity_completed || '-'} - {a.percentage_completed ? `${a.percentage_completed}%` : '-'} + {a.task_name} + {a.zone_area || '-'} + {a.quantity_completed || '-'} + {a.percentage_completed ? `${a.percentage_completed}%` : '-'} ))} @@ -127,26 +284,27 @@ export default function Show({ report }: any) { )} + {/* Materials Section */} {report.materials && report.materials.length > 0 && ( - - - Materials + + + Materials
- + - Material - Quantity - Condition + Material + Quantity + Condition {report.materials.map((m: any, i: number) => ( - {m.material_name} - {m.quantity_received || '-'} - {m.condition || '-'} + {m.material_name} + {m.quantity_received || '-'} + {m.condition || '-'} ))} @@ -155,28 +313,29 @@ export default function Show({ report }: any) { )} + {/* Manpower/Labor Section */} {report.labors && report.labors.length > 0 && ( - - - Manpower/Labor + + + Manpower/Labor
- + - Trade - Workers - Hours/Worker - Notes + Trade + Workers + Hours/Worker + Notes {report.labors.map((l: any, i: number) => ( - {l.trade} - {l.workers_count} - {l.hours || '-'} - {l.notes || '-'} + {l.trade} + {l.workers_count} + {l.hours || '-'} + {l.notes || '-'} ))} @@ -185,54 +344,27 @@ export default function Show({ report }: any) { )} + {/* Equipment Section */} {report.equipment && report.equipment.length > 0 && ( - - - Equipment + + + Equipment
- + - Equipment - Hours Used - Status + Equipment + Hours Used + Status {report.equipment.map((e: any, i: number) => ( - {e.equipment_name} - {e.hours_used || '-'} - {e.status || '-'} - - ))} - -
-
-
- )} - - {report.issues && report.issues.length > 0 && ( - - - Issues & Concern - - - - - - Type - Description - Delay Impact - - - - {report.issues.map((issue: any, i: number) => ( - - {issue.issue_type} - {issue.description} - {issue.delay_impact || '-'} + {e.equipment_name} + {e.hours_used || '-'} + {e.status || '-'} ))} @@ -241,18 +373,103 @@ export default function Show({ report }: any) { )} + {/* Superintendent Remarks */} {(report.remarks || report.work_accomplished) && ( - - - Superintendent Remarks + + + Superintendent Remarks - + {report.remarks || report.work_accomplished} )} + - + + {/* Resolve Compliance Modal */} + + + + + Issue Resolution Compliance Form + + + Record supervisor oversight and resolution notes for compliance tracking. + + + + {selectedIssue && ( +
+
+ Target Issue ({selectedIssue.issue_type}): +
+
+ {selectedIssue.description} +
+
+ )} + +
+
+ + setData('supervisor_name', e.target.value)} + placeholder="e.g. Engr. Marco Santos" + required + /> + {errors.supervisor_name &&

{errors.supervisor_name}

} +
+ +
+ + setData('supervisor_role', e.target.value)} + placeholder="e.g. Construction Supervisor, Technical Manager" + required + /> + {errors.supervisor_role &&

{errors.supervisor_role}

} +
+ +
+ +