158 lines
5.1 KiB
PHP
158 lines
5.1 KiB
PHP
<?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',
|
|
'achievements',
|
|
])
|
|
->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,
|
|
];
|
|
}
|
|
}
|