Merge pull request #3 from reihanrere/add-generate-pdf-library

add generate pdf & add some field on users
This commit is contained in:
Reihan Renaldi 2025-05-18 22:07:07 +07:00 committed by GitHub
commit f6efb0d61e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 1323 additions and 86 deletions

View File

@ -8,6 +8,7 @@ use App\Models\ClassRoom;
use App\Models\ClassSubject;
use App\Models\CompetencyAchievement;
use App\Models\ExtracurricularAssessment;
use App\Models\HomeRoomTeacher;
use App\Models\Student;
use App\Models\SchoolInformation;
use App\Models\Subject;
@ -17,6 +18,7 @@ use Filament\Pages\Page;
use Carbon\Carbon;
use function PHPUnit\Framework\isEmpty;
use function Symfony\Component\String\s;
use Barryvdh\DomPDF\Facade\Pdf;
Carbon::setLocale('id');
@ -40,6 +42,8 @@ class ReportPreview extends Page
public $izin = 0;
public $tanpa_keterangan = 0;
public $home_room_teacher;
public function mount()
{
$this->loadData();
@ -55,11 +59,10 @@ class ReportPreview extends Page
'tanpa_keterangan' => 'required|integer|min:0',
]);
$this->dispatch('notify', [
'title' => 'Berhasil',
'message' => 'Data ketidakhadiran diperbarui',
'type' => 'success'
]);
Notification::make()
->title('Data Kehadiran Disimpan')
->success()
->send();
}
protected function loadData() : void
@ -161,15 +164,14 @@ class ReportPreview extends Page
->toArray();
$this->table["extracurricular"] = $extracurricular;
}
public function downloadPdf()
{
$view = $this->render()->render();
$pdf = PDF::loadHTML($view)->setPaper('a4', 'landscape');
return response()->streamDownload(function () use ($pdf) {
echo $pdf->stream();
}, $this->student->full_name . '.pdf');
$homeRoom = HomeRoomTeacher::with(['teacher'])
->where('class_room_id', $this->class->id)
->where('academic_year_id', $this->academic_year->id)
->firstOrFail()
->toArray();
$this->home_room_teacher = $homeRoom ?? [];
}
public function extractClassLetter($className)

View File

@ -22,6 +22,16 @@ class SchoolInformation extends Page implements HasForms
protected static ?string $navigationGroup = 'Settings';
protected static ?int $navigationSort = 999; // Biar muncul paling bawah
public static function canAccess(): bool
{
return auth()->user()->hasAnyRole(['super_admin']);
}
public static function shouldRegisterNavigation(): bool
{
return auth()->user()->hasAnyRole(['super_admin']);
}
public ?array $data = [];
public ?SchoolInformationModel $record = null;

View File

@ -29,21 +29,43 @@ class UserResource extends Resource
Forms\Components\TextInput::make('name')
->required()
->maxLength(255),
Forms\Components\TextInput::make('email')
->email()
->required()
->maxLength(255),
Forms\Components\TextInput::make('password')
->password()
->required(fn (string $context): bool => $context === 'create')
->dehydrateStateUsing(fn ($state) => Hash::make($state))
->dehydrated(fn ($state) => filled($state))
->maxLength(255),
Forms\Components\Select::make('role')
->relationship('roles', 'name')
->preload()
->searchable()
->required(),
Forms\Components\TextInput::make('nip')
->label('NIP')
->maxLength(255),
Forms\Components\Select::make('gender')
->options([
'L' => 'L',
'P' => 'P',
]),
Forms\Components\DatePicker::make('birth_date'),
Forms\Components\TextInput::make('phone')
->tel()
->maxLength(255),
Forms\Components\Textarea::make('address')
->columnSpanFull(),
]);
}
@ -55,6 +77,18 @@ class UserResource extends Resource
->searchable(),
Tables\Columns\TextColumn::make('email')
->searchable(),
Tables\Columns\TextColumn::make('nip')
->label('NIP')
->searchable(),
Tables\Columns\TextColumn::make('gender')
->formatStateUsing(fn (string $state): string => ucfirst($state)),
Tables\Columns\TextColumn::make('birth_date')
->date()
->sortable(),
Tables\Columns\TextColumn::make('phone')
->searchable(),
Tables\Columns\TextColumn::make('roles.name')
->searchable(),
Tables\Columns\TextColumn::make('created_at')
->dateTime()
->sortable()
@ -63,7 +97,6 @@ class UserResource extends Resource
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('roles.name')->searchable(),
])
->filters([
//

View File

@ -0,0 +1,229 @@
<?php
namespace App\Http\Controllers;
use App\Models\AcademicYear;
use App\Models\Assessment;
use App\Models\ClassRoom;
use App\Models\CompetencyAchievement;
use App\Models\ExtracurricularAssessment;
use App\Models\HomeRoomTeacher;
use App\Models\SchoolInformation;
use App\Models\Student;
use Barryvdh\DomPDF\Facade\Pdf;
use Filament\Notifications\Notification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ReportPDFController extends Controller
{
public $student;
public $class;
public $class_name;
public $academic_year;
public $semester;
public $school_information;
public $table = [];
public $home_room_teacher;
public function reportPDF(Request $request)
{
$params = $request->query();
$defaultParams = ["studentId", "classId", "yearId", "semester", "sakit", "izin", "tanpa_keterangan"];
try {
$this->validateParameters($params, $defaultParams);
} catch (\Illuminate\Validation\ValidationException $e) {
Notification::make()
->title('Parameter tidak valid: ' . $e->getMessage())
->danger()
->send();
return back();
}
$this->student = Student::findOrFail($params["studentId"]);
$this->class = ClassRoom::findOrFail($params["classId"]);
$this->academic_year = AcademicYear::findOrFail($params["yearId"]);
$this->semester = $params["semester"];
$this->school_information = SchoolInformation::firstOrFail();
$this->class_name = $this->toRoman($this->class->class_level) . ' ' . $this->extractClassLetter($this->class->class_name);
$this->loadAssessment();
$data = [
'student' => $this->student,
'school_information' => $this->school_information,
'table' => $this->table,
'sakit' => $params['sakit'],
'izin' => $params['izin'],
'tanpa_keterangan' => $params['tanpa_keterangan'],
'class_name' => $this->class_name,
'semester' => $params['semester'],
'academic_year' => $this->academic_year,
'home_room_teacher' => $this->home_room_teacher,
];
$pdf = Pdf::loadView('print.report', $data)
->setPaper('a4', 'portrait')
->setOption('isRemoteEnabled', true)
->setOption('isHtml5ParserEnabled', true);
return response()->streamDownload(
function () use ($pdf) {
echo $pdf->stream();
},
'Rapor_' . $this->student->full_name . '.pdf'
);
}
protected function loadAssessment(): void
{
$student_id = $this->student->id;
$class_id = $this->class->id;
$semester = $this->semester;
$year_id = $this->academic_year->id;
// Assessment Mapping
$assessments = Assessment::where('semester', $this->semester)
->whereHas('teacherSubject', function($query) use ($class_id, $year_id) {
$query->where('academic_year_id', $year_id)
->where('class_id', $class_id);
})
->where('student_id', $this->student->id)
->with('teacherSubject.subject', 'student')
->get()
->map(function ($as) use ($class_id) {
$subject = $as->teacherSubject->subject ?? null;
if (!$subject) {
return null;
}
$competencyAchievements = CompetencyAchievement::where('class_room_id', $class_id)
->where('subject_id', $subject->id)
->where('min_score', '<=', $as->score)
->where('max_score', '>=', $as->score)
->first();
return [
"score" => $as->score,
"subject" => $subject->name,
"category" => $subject->category,
"competency_achievement" => $competencyAchievements ? $this->student->full_name . ' ' . $competencyAchievements->description : '-',
];
})
->filter() // Hapus null jika ada
->sortByDesc('subject')
->sortBy(function ($item) {
return $item['category'] === 'umum' ? 0 : 1;
})
->values()
->toArray();
$this->table["assessments"]['umum'] = array_filter($assessments, function ($item) {
return $item["category"] === "umum";
});
$this->table["assessments"]['muatan lokal'] = array_filter($assessments, function ($item) {
return $item["category"] === "muatan lokal";
});
$this->table["assessments"]['seni'] = array_filter($assessments, function ($item) {
return $item["category"] === "seni";
});
$extracurricular = ExtracurricularAssessment::with('classStudent','extracurricular')
->where('semester', $this->semester)
->whereHas('classStudent', function($query) use ($class_id, $year_id) {
$query->where('academic_year_id', $year_id)
->where('class_room_id', $class_id)
->where('student_id', $this->student->id);
})
->get()
->map(function ($as) {
return [
"name" => $as->extracurricular->name,
"predicate" => $as->predicate,
"description" => $as->description,
];
})
->toArray();
$this->table["extracurricular"] = $extracurricular;
$homeRoom = HomeRoomTeacher::with(['teacher'])
->where('class_room_id', $this->class->id)
->where('academic_year_id', $this->academic_year->id)
->firstOrFail()
->toArray();
$this->home_room_teacher = $homeRoom ?? [];
}
protected function validateParameters(array $params, array $allowedParams): void
{
$validator = Validator::make($params, [
'studentId' => 'required|integer',
'classId' => 'required|integer',
'yearId' => 'required|integer',
'semester' => 'required|in:first,second',
'sakit' => 'nullable|integer|min:0',
'izin' => 'nullable|integer|min:0',
'tanpa_keterangan' => 'nullable|integer|min:0',
]);
if ($validator->fails()) {
throw new \Illuminate\Validation\ValidationException($validator);
}
foreach ($params as $key => $value) {
if (!in_array($key, $allowedParams)) {
throw new \InvalidArgumentException("Parameter {$key} tidak valid");
}
}
}
public function extractClassLetter($className): string
{
preg_match('/[A-Z]+$/i', trim($className), $matches);
return $matches[0] ?? '';
}
public function toRoman($number): string
{
$map = [
'M' => 1000,
'CM' => 900,
'D' => 500,
'CD' => 400,
'C' => 100,
'XC' => 90,
'L' => 50,
'XL' => 40,
'X' => 10,
'IX' => 9,
'V' => 5,
'IV' => 4,
'I' => 1,
];
$returnValue = '';
while ($number > 0) {
foreach ($map as $roman => $int) {
if ($number >= $int) {
$number -= $int;
$returnValue .= $roman;
break;
}
}
}
return $returnValue;
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\AcademicYear;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class AcademicYearPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_academic::year');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, AcademicYear $academicYear): bool
{
return $user->can('view_academic::year');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_academic::year');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, AcademicYear $academicYear): bool
{
return $user->can('update_academic::year');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, AcademicYear $academicYear): bool
{
return $user->can('delete_academic::year');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, AcademicYear $academicYear): bool
{
return $user->can('restore_academic::year');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, AcademicYear $academicYear): bool
{
return $user->can('force_delete_academic::year');
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\ClassStudent;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class ClassStudentPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_class::student');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, ClassStudent $classStudent): bool
{
return $user->can('view_class::student');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_class::student');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, ClassStudent $classStudent): bool
{
return $user->can('update_class::student');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, ClassStudent $classStudent): bool
{
return $user->can('delete_class::student');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, ClassStudent $classStudent): bool
{
return $user->can('restore_class::student');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, ClassStudent $classStudent): bool
{
return $user->can('force_delete_class::student');
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\ClassSubject;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class ClassSubjectPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_class::subject');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, ClassSubject $classSubject): bool
{
return $user->can('view_class::subject');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_class::subject');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, ClassSubject $classSubject): bool
{
return $user->can('update_class::subject');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, ClassSubject $classSubject): bool
{
return $user->can('delete_class::subject');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, ClassSubject $classSubject): bool
{
return $user->can('restore_class::subject');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, ClassSubject $classSubject): bool
{
return $user->can('force_delete_class::subject');
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\CompetencyAchievement;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class CompetencyAchievementPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_competency::achievement');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, CompetencyAchievement $competencyAchievement): bool
{
return $user->can('view_competency::achievement');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_competency::achievement');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, CompetencyAchievement $competencyAchievement): bool
{
return $user->can('update_competency::achievement');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, CompetencyAchievement $competencyAchievement): bool
{
return $user->can('delete_competency::achievement');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, CompetencyAchievement $competencyAchievement): bool
{
return $user->can('restore_competency::achievement');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, CompetencyAchievement $competencyAchievement): bool
{
return $user->can('force_delete_competency::achievement');
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\ExtracurricularAssessment;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class ExtracurricularAssessmentPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_extracurricular::assessment');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, ExtracurricularAssessment $extracurricularAssessment): bool
{
return $user->can('view_extracurricular::assessment');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_extracurricular::assessment');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, ExtracurricularAssessment $extracurricularAssessment): bool
{
return $user->can('update_extracurricular::assessment');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, ExtracurricularAssessment $extracurricularAssessment): bool
{
return $user->can('delete_extracurricular::assessment');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, ExtracurricularAssessment $extracurricularAssessment): bool
{
return $user->can('restore_extracurricular::assessment');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, ExtracurricularAssessment $extracurricularAssessment): bool
{
return $user->can('force_delete_extracurricular::assessment');
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\HomeRoomTeacher;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class HomeRoomTeacherPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_home::rome::teacher');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, HomeRoomTeacher $homeRomeTeacher): bool
{
return $user->can('view_home::rome::teacher');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_home::rome::teacher');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, HomeRoomTeacher $homeRomeTeacher): bool
{
return $user->can('update_home::rome::teacher');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, HomeRoomTeacher $homeRomeTeacher): bool
{
return $user->can('delete_home::rome::teacher');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, HomeRoomTeacher $homeRomeTeacher): bool
{
return $user->can('restore_home::rome::teacher');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, HomeRoomTeacher $homeRomeTeacher): bool
{
return $user->can('force_delete_home::rome::teacher');
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\SchoolInformation;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class SchoolInformationPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_school::information');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, SchoolInformation $schoolInformation): bool
{
return $user->can('view_school::information');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_school::information');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, SchoolInformation $schoolInformation): bool
{
return $user->can('update_school::information');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, SchoolInformation $schoolInformation): bool
{
return $user->can('delete_school::information');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, SchoolInformation $schoolInformation): bool
{
return $user->can('restore_school::information');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, SchoolInformation $schoolInformation): bool
{
return $user->can('force_delete_school::information');
}
}

View File

@ -1,65 +0,0 @@
<?php
namespace App\Policies;
use App\Models\SubjectScope;
use App\Models\User;
class SubjectScopePolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_subject::scope');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, SubjectScope $subjectScope): bool
{
return $user->can('view_subject::scope');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_subject::scope');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, SubjectScope $subjectScope): bool
{
return $user->can('update_subject::scope');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, SubjectScope $subjectScope): bool
{
return $user->can('delete_subject::scope');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, SubjectScope $subjectScope): bool
{
return $user->can('restore_subject::scope');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, SubjectScope $subjectScope): bool
{
return $user->can('force_delete_subject::scope');
}
}

View File

@ -56,6 +56,7 @@ class AdminPanelProvider extends PanelProvider
])
->plugins([
\BezhanSalleh\FilamentShield\FilamentShieldPlugin::make(),
]);
])
->brandName('Sistem Akademik Sekolah');
}
}

View File

@ -7,6 +7,7 @@
"license": "MIT",
"require": {
"php": "^8.2",
"barryvdh/laravel-dompdf": "^3.1",
"bezhansalleh/filament-shield": "^3.3",
"laravel/framework": "^11.31",
"laravel/tinker": "^2.9"

299
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6ab55b3e872edca1f0857b78d520564b",
"content-hash": "c3a721ae1c529499b996247d90300571",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@ -72,6 +72,83 @@
},
"time": "2025-04-06T06:54:34+00:00"
},
{
"name": "barryvdh/laravel-dompdf",
"version": "v3.1.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-dompdf.git",
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
"shasum": ""
},
"require": {
"dompdf/dompdf": "^3.0",
"illuminate/support": "^9|^10|^11|^12",
"php": "^8.1"
},
"require-dev": {
"larastan/larastan": "^2.7|^3.0",
"orchestra/testbench": "^7|^8|^9|^10",
"phpro/grumphp": "^2.5",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"PDF": "Barryvdh\\DomPDF\\Facade\\Pdf",
"Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf"
},
"providers": [
"Barryvdh\\DomPDF\\ServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Barryvdh\\DomPDF\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "A DOMPDF Wrapper for Laravel",
"keywords": [
"dompdf",
"laravel",
"pdf"
],
"support": {
"issues": "https://github.com/barryvdh/laravel-dompdf/issues",
"source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.1"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2025-02-13T15:07:54+00:00"
},
{
"name": "bezhansalleh/filament-shield",
"version": "3.3.5",
@ -940,6 +1017,161 @@
],
"time": "2024-02-05T11:56:58+00:00"
},
{
"name": "dompdf/dompdf",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
"reference": "a51bd7a063a65499446919286fb18b518177155a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/a51bd7a063a65499446919286fb18b518177155a",
"reference": "a51bd7a063a65499446919286fb18b518177155a",
"shasum": ""
},
"require": {
"dompdf/php-font-lib": "^1.0.0",
"dompdf/php-svg-lib": "^1.0.0",
"ext-dom": "*",
"ext-mbstring": "*",
"masterminds/html5": "^2.0",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"ext-gd": "*",
"ext-json": "*",
"ext-zip": "*",
"mockery/mockery": "^1.3",
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0"
},
"suggest": {
"ext-gd": "Needed to process images",
"ext-gmagick": "Improves image processing performance",
"ext-imagick": "Improves image processing performance",
"ext-zlib": "Needed for pdf stream compression"
},
"type": "library",
"autoload": {
"psr-4": {
"Dompdf\\": "src/"
},
"classmap": [
"lib/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "The Dompdf Community",
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
}
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
"source": "https://github.com/dompdf/dompdf/tree/v3.1.0"
},
"time": "2025-01-15T14:09:04+00:00"
},
{
"name": "dompdf/php-font-lib",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-font-lib.git",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6"
},
"type": "library",
"autoload": {
"psr-4": {
"FontLib\\": "src/FontLib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "The FontLib Community",
"homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/dompdf/php-font-lib",
"support": {
"issues": "https://github.com/dompdf/php-font-lib/issues",
"source": "https://github.com/dompdf/php-font-lib/tree/1.0.1"
},
"time": "2024-12-02T14:37:59+00:00"
},
{
"name": "dompdf/php-svg-lib",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-svg-lib.git",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0",
"sabberworm/php-css-parser": "^8.4"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Svg\\": "src/Svg"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The SvgLib Community",
"homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/dompdf/php-svg-lib",
"support": {
"issues": "https://github.com/dompdf/php-svg-lib/issues",
"source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0"
},
"time": "2024-04-29T13:26:35+00:00"
},
{
"name": "dragonmantank/cron-expression",
"version": "v3.4.0",
@ -4797,6 +5029,71 @@
],
"time": "2025-02-25T09:09:36+00:00"
},
{
"name": "sabberworm/php-css-parser",
"version": "v8.8.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
"reference": "3de493bdddfd1f051249af725c7e0d2c38fed740"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/3de493bdddfd1f051249af725c7e0d2c38fed740",
"reference": "3de493bdddfd1f051249af725c7e0d2c38fed740",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "9.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Sabberworm\\CSS\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raphael Schweikert"
},
{
"name": "Oliver Klee",
"email": "github@oliverklee.de"
},
{
"name": "Jake Hotson",
"email": "jake.github@qzdesign.co.uk"
}
],
"description": "Parser for CSS Files written in PHP",
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
"keywords": [
"css",
"parser",
"stylesheet"
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.8.0"
},
"time": "2025-03-23T17:59:05+00:00"
},
{
"name": "spatie/color",
"version": "1.8.0",

View File

@ -173,8 +173,8 @@
Depok, {{ \Carbon\Carbon::now()->translatedFormat('d F Y') }} <br>
Wali Kelas {{ $this->class_name ?? "-" }} <br><br><br>
<strong>Siti Mawarni, S.Pd</strong><br>
NIP. 197502021990121001
<strong>{{ $home_room_teacher["teacher"]["name"] ?? '-' }}</strong><br>
NIP. {{ $home_room_teacher["teacher"]["nip"] ?? '-' }}
</div>
</div>
@ -223,9 +223,19 @@
</x-filament::button>
<x-filament::button
wire:click="downloadPdf"
class="w-full">
Generate Report
tag="a"
href="{{ route('report.pdf', [
'studentId' => $this->student->id,
'classId' => $this->class->id,
'yearId' => $this->academic_year->id,
'semester' => $this->semester,
'sakit' => $this->sakit,
'izin' => $this->izin,
'tanpa_keterangan' => $this->tanpa_keterangan,
]) }}"
target="_blank"
class="w-full">
Download PDF
</x-filament::button>
</div>
</div>

View File

@ -0,0 +1,254 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<title>Laporan Hasil Belajar</title>
<style>
body {
font-family: 'Times New Roman', Times, serif;
font-size: 14px;
margin: 40px;
}
.text-center {
text-align: center;
}
.uppercase {
text-transform: uppercase;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.no-full {
width: 256px;
border-collapse: collapse;
margin-top: 10px;
}
table, th, td {
border: 1px solid #000;
}
td, th {
padding: 6px;
vertical-align: top;
}
.no-border {
border: none !important;
text-align: left;
}
.signature-section {
margin-top: 60px;
}
.flex {
display: flex;
justify-content: space-between;
}
.w-50 {
width: 48%;
}
.mt-20 {
margin-top: 80px;
}
.text-right {
text-align: right;
}
.mb-4 {
margin-bottom: 1rem;
}
.underline {
display: inline-block;
border-bottom: 1px solid #000;
width: 200px;
margin-top: 30px;
}
</style>
</head>
<body>
<div class="text-center mb-4">
<h2 class="uppercase">Laporan Hasil Belajar</h2>
<h3 class="uppercase">(RAPOR)</h3>
</div>
<table class="no-border">
<tr class="no-border">
<td class="no-border">Nama Peserta Didik</td>
<td class="no-border">: {{ $student->full_name }}</td>
<td class="no-border">Kelas</td>
<td class="no-border">: {{ $class_name }}</td>
</tr>
<tr class="no-border">
<td class="no-border">NISN/NIS</td>
<td class="no-border">: {{ $student->nisn }} / {{ $student->nis }}</td>
<td class="no-border">Fase</td>
<td class="no-border">: -</td>
</tr>
<tr class="no-border">
<td class="no-border">Sekolah</td>
<td class="no-border">: {{ $school_information->school_name }}</td>
<td class="no-border">Semester</td>
<td class="no-border">: {{ $semester === 'first' ? "I" : "II"}}</td>
</tr>
<tr class="no-border">
<td class="no-border">Alamat</td>
<td class="no-border">: {{ $school_information->address }}</td>
<td class="no-border">Tahun Ajaran</td>
<td class="no-border">: {{ $academic_year->name }}</td>
</tr>
</table>
@php
$i = 1;
@endphp
<table>
<tr>
<th style="width: 2rem">No</th>
<th style="width: 185px">Muatan Pelajaran</th>
<th style="width: 100px" class="text-center">Nilai Akhir</th>
<th>Capaian Kompetensi</th>
</tr>
@if(!empty($table['assessments']['umum']))
@foreach($table['assessments']['umum'] as $subjects => $subject)
<tr>
<td>{{ $i }}</td>
<td>{{ $subject["subject"] }}</td>
<td class="text-center">{{ $subject["score"] }}</td>
<td>{{ $subject["competency_achievement"] }}</td>
</tr>
@php
$i++;
@endphp
@endforeach
@endif
@if(!empty($table['assessments']['seni']))
<tr>
<td>{{ $i }}</td>
<td colspan="3">Seni</td>
</tr>
@php $charIndex = 0; @endphp
@foreach($table['assessments']['seni'] as $subjects => $subject)
<tr>
<td>{{ chr(97 + $charIndex) }}</td>
<td>{{ $subject["subject"] }}</td>
<td class="text-center">{{ $subject["score"] }}</td>
<td>{{ $subject["competency_achievement"] }}</td>
</tr>
@php
$i++;
@endphp
@endforeach
@endif
</table>
<br><strong>Muatan Lokal</strong>
<table>
<tr>
<th style="width: 2rem">No</th>
<th style="width: 185px">Muatan Pelajaran</th>
<th style="width: 100px">Nilai Akhir</th>
<th>Capaian Kompetensi</th>
</tr>
@if(!empty($table['assessments']['muatan lokal']))
@foreach($table['assessments']['muatan lokal'] as $subjects => $subject)
<tr>
<td>{{ $i }}</td>
<td>{{ $subject["subject"] }}</td>
<td class="text-center">{{ $subject["score"] }}</td>
<td>{{ $subject["competency_achievement"] }}</td>
</tr>
@php
$i++;
@endphp
@endforeach
@endif
</table>
<table>
<tr>
<th style="width: 2rem">No</th>
<th style="width: 185px">Ekstrakurikuler</th>
<th style="width: 100px">Predikat</th>
<th>Keterangan</th>
</tr>
@php
$i = 1;
@endphp
@if(!empty($table["extracurricular"]))
@foreach($table['extracurricular'] as $subjects => $subject)
<tr>
<td class=>{{ $i }}</td>
<td class=>{{ $subject["name"] }}</td>
<td class='text-center'>{{ $subject["predicate"] }}</td>
<td class=>{{ $subject["description"] }}</td>
</tr>
@php
$i++;
@endphp
@endforeach
@else
<tr>
<td class=></td>
<td class=></td>
<td class=></td>
<td class=></td>
</tr>
@endif
</table>
<table class="no-full">
<tr>
<td colspan="2" style="text-align: center">
Ketidakhadiran
</td>
</tr>
<tr><td>Sakit</td><td>: {{ $sakit ?? 0 }} hari</td></tr>
<tr><td>Izin</td><td>: {{ $izin ?? 0 }} hari</td></tr>
<tr><td>Tanpa Keterangan</td><td>: {{ $tanpa_keterangan ?? 0 }} hari</td></tr>
</table>
<br>
<table class="no-border" style="margin-top: 60px;">
<tr class="no-border">
<td class="no-border" style="text-align: center;">
Orang Tua / Wali<br><br><br><br>
<span class="underline"></span>
</td>
<td class="no-border" style="text-align: center;">
@php
\Carbon\Carbon::setLocale('id');
@endphp
Depok, {{ \Carbon\Carbon::now()->translatedFormat('d F Y') }}<br>
Wali Kelas {{ $class_name }}<br><br><br>
<strong>{{ $home_room_teacher["teacher"]["name"] ?? '-' }}</strong><br>
NIP. {{ $home_room_teacher["teacher"]["nip"] ?? '-' }}
</td>
</tr>
</table>
<div class="text-center mt-20">
Mengetahui,<br>
Kepala Sekolah<br><br><br>
<strong>{{ $school_information->headmaster_name }}</strong><br>
NIP: {{ $school_information->headmaster_nip }}
</div>
</body>
</html>

View File

@ -1,7 +1,10 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ReportPDFController;
Route::get('/', function () {
return view('welcome');
});
Route::get('/report-pdf', [ReportPDFController::class, 'reportPDF'])->name('report.pdf');