Initial commit
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\DocumentType;
|
||||
use App\Models\Student;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class PublicStudentLookupController extends Controller
|
||||
{
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$query = trim((string) $request->get('q'));
|
||||
$students = collect();
|
||||
$selectedStudent = null;
|
||||
$requiredDocumentSummary = null;
|
||||
$matchedBy = null;
|
||||
$selectedStudentId = $request->integer('student');
|
||||
|
||||
if ($query !== '') {
|
||||
$students = Student::query()
|
||||
->with([
|
||||
'academicYear',
|
||||
'department',
|
||||
'classRoom',
|
||||
'documents.documentType',
|
||||
])
|
||||
->where(function ($builder) use ($query) {
|
||||
$builder->where('student_number', 'like', "%{$query}%")
|
||||
->orWhere('nisn', 'like', "%{$query}%")
|
||||
->orWhere('full_name', 'like', "%{$query}%");
|
||||
})
|
||||
->orderByRaw('CASE WHEN student_number = ? THEN 0 WHEN nisn = ? THEN 1 WHEN full_name = ? THEN 2 ELSE 3 END', [$query, $query, $query])
|
||||
->orderBy('full_name')
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
$selectedStudent = $students->firstWhere('id', $selectedStudentId) ?? $students->first();
|
||||
|
||||
if ($selectedStudent) {
|
||||
$requiredDocumentSummary = $this->buildRequiredDocumentSummary($selectedStudent);
|
||||
$matchedBy = $this->detectMatchedBy($selectedStudent, $query);
|
||||
}
|
||||
|
||||
$students->each(function (Student $student) {
|
||||
$student->public_photo_url = $this->resolvePhotoUrl($student);
|
||||
});
|
||||
}
|
||||
|
||||
return view('public.student-lookup', [
|
||||
'query' => $query,
|
||||
'students' => $students,
|
||||
'student' => $selectedStudent,
|
||||
'requiredDocumentSummary' => $requiredDocumentSummary,
|
||||
'matchedBy' => $matchedBy,
|
||||
'selectedStudentId' => $selectedStudent?->id,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function detectMatchedBy(Student $student, string $query): ?string
|
||||
{
|
||||
if ($student->student_number && strcasecmp($student->student_number, $query) === 0) {
|
||||
return 'NIS';
|
||||
}
|
||||
|
||||
if ($student->nisn && strcasecmp($student->nisn, $query) === 0) {
|
||||
return 'NISN';
|
||||
}
|
||||
|
||||
return 'Nama';
|
||||
}
|
||||
|
||||
protected function resolvePhotoUrl(Student $student): ?string
|
||||
{
|
||||
$photoDocument = $student->documents
|
||||
->filter(function ($document) {
|
||||
$code = strtoupper((string) ($document->documentType?->code ?? ''));
|
||||
$name = strtolower((string) ($document->documentType?->name ?? ''));
|
||||
|
||||
return $code === 'PAS_FOTO'
|
||||
|| $code === 'PASFOTO'
|
||||
|| str_contains($name, 'pas foto')
|
||||
|| str_contains($name, 'pasfoto');
|
||||
})
|
||||
->sortByDesc('created_at')
|
||||
->first();
|
||||
|
||||
if (! $photoDocument?->file_path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return asset('storage/' . $photoDocument->file_path);
|
||||
}
|
||||
|
||||
protected function buildRequiredDocumentSummary(Student $student): array
|
||||
{
|
||||
$requiredTypes = DocumentType::query()
|
||||
->where('is_active', true)
|
||||
->where('is_required', true)
|
||||
->get();
|
||||
|
||||
if ($requiredTypes->isEmpty()) {
|
||||
return [
|
||||
'total' => 0,
|
||||
'uploaded' => 0,
|
||||
'verified' => 0,
|
||||
'missing' => 0,
|
||||
'unverified' => 0,
|
||||
'percentage' => 100,
|
||||
'verification_percentage' => 100,
|
||||
'missing_items' => collect(),
|
||||
'unverified_items' => collect(),
|
||||
];
|
||||
}
|
||||
|
||||
$documents = $student->documents;
|
||||
|
||||
$uploaded = 0;
|
||||
$verified = 0;
|
||||
$missingItems = collect();
|
||||
$unverifiedItems = collect();
|
||||
|
||||
foreach ($requiredTypes as $type) {
|
||||
$matchingDocuments = $documents->where('document_type_id', $type->id);
|
||||
$latestDocument = $matchingDocuments->sortByDesc('created_at')->first();
|
||||
|
||||
if (! $latestDocument) {
|
||||
$missingItems->push($type->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
$uploaded++;
|
||||
|
||||
if ($latestDocument->verification_status === 'valid') {
|
||||
$verified++;
|
||||
} else {
|
||||
$unverifiedItems->push($type->name);
|
||||
}
|
||||
}
|
||||
|
||||
$total = $requiredTypes->count();
|
||||
|
||||
return [
|
||||
'total' => $total,
|
||||
'uploaded' => $uploaded,
|
||||
'verified' => $verified,
|
||||
'missing' => max($total - $uploaded, 0),
|
||||
'unverified' => max($total - $verified, 0),
|
||||
'percentage' => $total > 0 ? (int) round(($uploaded / $total) * 100) : 100,
|
||||
'verification_percentage' => $total > 0 ? (int) round(($verified / $total) * 100) : 100,
|
||||
'missing_items' => $missingItems,
|
||||
'unverified_items' => $unverifiedItems,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user