From c77f6ae60316a8a823f4f61a301cd1101a2d4468 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sun, 30 Aug 2026 10:56:23 +0700 Subject: [PATCH] feat: add export functionality for lecturers and students, including styled heading rows in exports --- app/Exports/Concerns/StyledHeadingRow.php | 49 +++ app/Exports/LecturersExport.php | 68 ++++ app/Exports/StudentsExport.php | 73 ++++ .../Admin/Users/LecturerController.php | 12 + .../Admin/Users/StudentController.php | 13 + app/Services/Admin/Users/LecturerService.php | 19 +- app/Services/Admin/Users/StudentService.php | 19 +- composer.json | 1 + composer.lock | 383 +++++++++++++++++- .../js/pages/admin/users/lecturers/index.tsx | 27 +- .../js/pages/admin/users/students/index.tsx | 27 +- routes/admin.php | 2 + 12 files changed, 672 insertions(+), 21 deletions(-) create mode 100644 app/Exports/Concerns/StyledHeadingRow.php create mode 100644 app/Exports/LecturersExport.php create mode 100644 app/Exports/StudentsExport.php diff --git a/app/Exports/Concerns/StyledHeadingRow.php b/app/Exports/Concerns/StyledHeadingRow.php new file mode 100644 index 0000000..96c598b --- /dev/null +++ b/app/Exports/Concerns/StyledHeadingRow.php @@ -0,0 +1,49 @@ + [ + 'font' => ['bold' => true, 'color' => ['rgb' => '733E0A']], + 'fill' => [ + 'fillType' => Fill::FILL_SOLID, + 'startColor' => ['rgb' => 'FDC700'], + ], + 'alignment' => [ + 'horizontal' => Alignment::HORIZONTAL_LEFT, + 'vertical' => Alignment::VERTICAL_CENTER, + ], + ], + ]; + } + + public function registerEvents(): array + { + return [ + AfterSheet::class => function (AfterSheet $event) { + $sheet = $event->sheet->getDelegate(); + $lastColumn = $sheet->getHighestColumn(); + + $sheet->insertNewRowBefore(1, 1); + $sheet->mergeCells("A1:{$lastColumn}1"); + $sheet->setCellValue('A1', $this->title().' '.config('app.name')); + $sheet->getRowDimension(1)->setRowHeight(24); + + $sheet->getStyle('A1')->getFont()->setBold(true)->setSize(14); + $sheet->getStyle('A1')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); + $sheet->getStyle('A1')->getAlignment()->setVertical(Alignment::VERTICAL_CENTER); + }, + ]; + } +} diff --git a/app/Exports/LecturersExport.php b/app/Exports/LecturersExport.php new file mode 100644 index 0000000..1281cd8 --- /dev/null +++ b/app/Exports/LecturersExport.php @@ -0,0 +1,68 @@ +forExport($this->search, $this->gender, $this->departmentId); + } + + public function headings(): array + { + return [ + 'NIDN', + 'Nama Lengkap', + 'Username', + 'Email', + 'Nomor Telepon', + 'Jurusan', + 'Jenis Kelamin', + 'Tempat Lahir', + 'Tanggal Lahir', + 'Alamat', + ]; + } + + public function map($user): array + { + /** @var User $user */ + return [ + $user->lecturer?->lecturer_number ?? '-', + $user->profile?->full_name ?? '-', + $user->username, + $user->email, + $user->profile?->phone_number ?? '-', + $user->lecturer?->department?->name ?? '-', + $user->profile?->gender?->label() ?? '-', + $user->profile?->birth_place ?? '-', + $user->profile?->birth_date?->format('d M Y') ?? '-', + $user->profile?->address ?? '-', + ]; + } +} diff --git a/app/Exports/StudentsExport.php b/app/Exports/StudentsExport.php new file mode 100644 index 0000000..419df0b --- /dev/null +++ b/app/Exports/StudentsExport.php @@ -0,0 +1,73 @@ +forExport($this->search, $this->gender, $this->departmentId, $this->status); + } + + public function headings(): array + { + return [ + 'NIM', + 'Nama Lengkap', + 'Username', + 'Email', + 'Nomor Telepon', + 'Jurusan', + 'Angkatan', + 'Status', + 'Jenis Kelamin', + 'Tempat Lahir', + 'Tanggal Lahir', + 'Alamat', + ]; + } + + public function map($user): array + { + /** @var User $user */ + return [ + $user->student?->student_number ?? '-', + $user->profile?->full_name ?? '-', + $user->username, + $user->email, + $user->profile?->phone_number ?? '-', + $user->student?->department?->name ?? '-', + $user->student?->enrollment_year ?? '-', + $user->student?->status?->label() ?? '-', + $user->profile?->gender?->label() ?? '-', + $user->profile?->birth_place ?? '-', + $user->profile?->birth_date?->format('d M Y') ?? '-', + $user->profile?->address ?? '-', + ]; + } +} diff --git a/app/Http/Controllers/Admin/Users/LecturerController.php b/app/Http/Controllers/Admin/Users/LecturerController.php index b5db34f..1d000f9 100644 --- a/app/Http/Controllers/Admin/Users/LecturerController.php +++ b/app/Http/Controllers/Admin/Users/LecturerController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Admin\Users; +use App\Exports\LecturersExport; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Users\LecturerRequest; use App\Http\Requests\PaginatedRequest; @@ -11,6 +12,8 @@ use Illuminate\Http\RedirectResponse; use Inertia\Inertia; use Inertia\Response; +use Maatwebsite\Excel\Facades\Excel; +use Symfony\Component\HttpFoundation\BinaryFileResponse; class LecturerController extends Controller { @@ -80,4 +83,13 @@ public function resetPassword(User $user): RedirectResponse return Inertia::flash('toast', ['type' => 'success', 'message' => 'Kata sandi berhasil direset.'])->back(); } + + public function export(PaginatedRequest $request): BinaryFileResponse + { + return Excel::download(new LecturersExport( + search: $request->validated('search') ?? '', + gender: $request->validated('gender'), + departmentId: $request->validated('department_id'), + ), 'dosen.xlsx'); + } } diff --git a/app/Http/Controllers/Admin/Users/StudentController.php b/app/Http/Controllers/Admin/Users/StudentController.php index c568377..ff9ebe2 100644 --- a/app/Http/Controllers/Admin/Users/StudentController.php +++ b/app/Http/Controllers/Admin/Users/StudentController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Admin\Users; use App\Enums\StudentStatus; +use App\Exports\StudentsExport; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\Users\StudentRequest; use App\Http\Requests\Admin\Users\StudentStatusRequest; @@ -14,6 +15,8 @@ use Illuminate\Http\RedirectResponse; use Inertia\Inertia; use Inertia\Response; +use Maatwebsite\Excel\Facades\Excel; +use Symfony\Component\HttpFoundation\BinaryFileResponse; class StudentController extends Controller { @@ -95,4 +98,14 @@ public function updateStatus(StudentStatusRequest $request, User $user): Redirec return Inertia::flash('toast', ['type' => 'success', 'message' => 'Status mahasiswa berhasil diperbarui.'])->back(); } + + public function export(PaginatedRequest $request): BinaryFileResponse + { + return Excel::download(new StudentsExport( + search: $request->validated('search') ?? '', + gender: $request->validated('gender'), + departmentId: $request->validated('department_id'), + status: $request->validated('status'), + ), 'mahasiswa.xlsx'); + } } diff --git a/app/Services/Admin/Users/LecturerService.php b/app/Services/Admin/Users/LecturerService.php index fa2fb9d..7bc4f57 100644 --- a/app/Services/Admin/Users/LecturerService.php +++ b/app/Services/Admin/Users/LecturerService.php @@ -5,6 +5,7 @@ use App\Models\Lecturer; use App\Models\User; use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; @@ -17,6 +18,20 @@ public function getAllForSelect(): Collection } public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', ?string $gender = null, ?int $departmentId = null): LengthAwarePaginator + { + return $this->filteredQuery($search, $gender, $departmentId) + ->orderBy($sort, $direction) + ->paginate($perPage); + } + + public function forExport(string $search = '', ?string $gender = null, ?int $departmentId = null): Collection + { + return $this->filteredQuery($search, $gender, $departmentId) + ->orderBy('created_at', 'desc') + ->get(); + } + + private function filteredQuery(string $search, ?string $gender, ?int $departmentId): Builder { return User::with(['profile', 'lecturer.department']) ->whereHas('roles', fn ($q) => $q->where('name', 'dosen')) @@ -27,9 +42,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = ->orWhereHas('lecturer', fn ($q) => $q->where('lecturer_number', 'like', "%{$search}%")); })) ->when($gender, fn ($q) => $q->whereHas('profile', fn ($q) => $q->where('gender', $gender))) - ->when($departmentId, fn ($q) => $q->whereHas('lecturer', fn ($q) => $q->where('department_id', $departmentId))) - ->orderBy($sort, $direction) - ->paginate($perPage); + ->when($departmentId, fn ($q) => $q->whereHas('lecturer', fn ($q) => $q->where('department_id', $departmentId))); } public function create(array $data): User diff --git a/app/Services/Admin/Users/StudentService.php b/app/Services/Admin/Users/StudentService.php index a384083..45cd2f0 100644 --- a/app/Services/Admin/Users/StudentService.php +++ b/app/Services/Admin/Users/StudentService.php @@ -5,6 +5,7 @@ use App\Models\Student; use App\Models\User; use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; @@ -17,6 +18,20 @@ public function getAllForSelect(): Collection } public function paginated(int $perPage = 25, string $search = '', string $sort = 'created_at', string $direction = 'desc', ?string $gender = null, ?int $departmentId = null, ?string $status = null): LengthAwarePaginator + { + return $this->filteredQuery($search, $gender, $departmentId, $status) + ->orderBy($sort, $direction) + ->paginate($perPage); + } + + public function forExport(string $search = '', ?string $gender = null, ?int $departmentId = null, ?string $status = null): Collection + { + return $this->filteredQuery($search, $gender, $departmentId, $status) + ->orderBy('created_at', 'desc') + ->get(); + } + + private function filteredQuery(string $search, ?string $gender, ?int $departmentId, ?string $status): Builder { return User::with(['profile', 'student.department']) ->whereHas('roles', fn ($q) => $q->where('name', 'mahasiswa')) @@ -28,9 +43,7 @@ public function paginated(int $perPage = 25, string $search = '', string $sort = })) ->when($gender, fn ($q) => $q->whereHas('profile', fn ($q) => $q->where('gender', $gender))) ->when($departmentId, fn ($q) => $q->whereHas('student', fn ($q) => $q->where('department_id', $departmentId))) - ->when($status, fn ($q) => $q->whereHas('student', fn ($q) => $q->where('status', $status))) - ->orderBy($sort, $direction) - ->paginate($perPage); + ->when($status, fn ($q) => $q->whereHas('student', fn ($q) => $q->where('status', $status))); } public function create(array $data): User diff --git a/composer.json b/composer.json index f5e36e7..63522a7 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ "laravel/framework": "^13.17", "laravel/tinker": "^3.0", "laravel/wayfinder": "^0.1.14", + "maatwebsite/excel": "^4.0", "spatie/laravel-medialibrary": "^11.23", "spatie/laravel-permission": "^8.3", "symfony/html-sanitizer": "^8.1" diff --git a/composer.lock b/composer.lock index c92fa50..92d6c30 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "3fabe2a835a47ecc56faf22b029cda13", + "content-hash": "379c97a9e3b22c91d1427f44e711a975", "packages": [ { "name": "bacon/bacon-qr-code", @@ -189,6 +189,82 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "composer/pcre", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<2.2.2" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2026-06-07T11:47:49+00:00" + }, { "name": "composer/semver", "version": "3.4.4", @@ -2581,6 +2657,95 @@ ], "time": "2026-03-08T20:05:35+00:00" }, + { + "name": "maatwebsite/excel", + "version": "4.0.2", + "source": { + "type": "git", + "url": "https://github.com/SpartnerNL/Laravel-Excel.git", + "reference": "3f3e73956f87e3d229b244cec8cbc3bbda4fc8dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/3f3e73956f87e3d229b244cec8cbc3bbda4fc8dd", + "reference": "3f3e73956f87e3d229b244cec8cbc3bbda4fc8dd", + "shasum": "" + }, + "require": { + "composer/semver": "^3.4", + "illuminate/support": "^12.0 || ^13.0", + "php": "^8.3", + "phpoffice/phpspreadsheet": "^5.8", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "brianium/paratest": "^7.20", + "driftingly/rector-laravel": "^2.5", + "ext-sqlite3": "*", + "larastan/larastan": "^3.10", + "laravel/pint": "^1.29", + "laravel/scout": "^10.25 || ^11.2", + "orchestra/testbench": "^10.11 || ^11.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-mockery": "^2.0", + "phpunit/phpunit": "^12.5 || ~13.1.14", + "predis/predis": "^2.3 || ^3.0", + "rector/rector": "^2.4.2" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Excel": "Maatwebsite\\Excel\\Facades\\Excel" + }, + "providers": [ + "Maatwebsite\\Excel\\ExcelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Maatwebsite\\Excel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Patrick Brouwers", + "email": "patrick@spartner.nl" + } + ], + "description": "Supercharged Excel exports and imports in Laravel", + "keywords": [ + "PHPExcel", + "batch", + "csv", + "excel", + "export", + "import", + "laravel", + "php", + "phpspreadsheet" + ], + "support": { + "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/4.0.2" + }, + "funding": [ + { + "url": "https://laravel-excel.com/commercial-support", + "type": "custom" + }, + { + "url": "https://github.com/patrickbrouwers", + "type": "github" + } + ], + "time": "2026-08-24T13:06:48+00:00" + }, { "name": "maennchen/zipstream-php", "version": "3.2.2", @@ -2659,6 +2824,113 @@ ], "time": "2026-04-11T18:38:28+00:00" }, + { + "name": "markbaker/complex", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPComplex.git", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPComplex/zipball/95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "reference": "95c56caa1cf5c766ad6d65b6344b807c1e8405b9", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Complex\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@lange.demon.co.uk" + } + ], + "description": "PHP Class for working with complex numbers", + "homepage": "https://github.com/MarkBaker/PHPComplex", + "keywords": [ + "complex", + "mathematics" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPComplex/issues", + "source": "https://github.com/MarkBaker/PHPComplex/tree/3.0.2" + }, + "time": "2022-12-06T16:21:08+00:00" + }, + { + "name": "markbaker/matrix", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/MarkBaker/PHPMatrix.git", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/MarkBaker/PHPMatrix/zipball/728434227fe21be27ff6d86621a1b13107a2562c", + "reference": "728434227fe21be27ff6d86621a1b13107a2562c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-master", + "phpcompatibility/php-compatibility": "^9.3", + "phpdocumentor/phpdocumentor": "2.*", + "phploc/phploc": "^4.0", + "phpmd/phpmd": "2.*", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "sebastian/phpcpd": "^4.0", + "squizlabs/php_codesniffer": "^3.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "Matrix\\": "classes/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Baker", + "email": "mark@demon-angel.eu" + } + ], + "description": "PHP Class for working with matrices", + "homepage": "https://github.com/MarkBaker/PHPMatrix", + "keywords": [ + "mathematics", + "matrix", + "vector" + ], + "support": { + "issues": "https://github.com/MarkBaker/PHPMatrix/issues", + "source": "https://github.com/MarkBaker/PHPMatrix/tree/3.0.1" + }, + "time": "2022-12-02T22:17:43+00:00" + }, { "name": "monolog/monolog", "version": "3.10.0", @@ -3414,6 +3686,115 @@ }, "time": "2026-01-06T21:53:42+00:00" }, + { + "name": "phpoffice/phpspreadsheet", + "version": "5.9.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", + "reference": "05e99ebf61238a70227b4d9cc02d0030d34f6339" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/05e99ebf61238a70227b4d9cc02d0030d34f6339", + "reference": "05e99ebf61238a70227b4d9cc02d0030d34f6339", + "shasum": "" + }, + "require": { + "composer/pcre": "^1||^2||^3", + "ext-ctype": "*", + "ext-dom": "*", + "ext-fileinfo": "*", + "ext-filter": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-simplexml": "*", + "ext-xml": "*", + "ext-xmlreader": "*", + "ext-xmlwriter": "*", + "ext-zip": "*", + "ext-zlib": "*", + "maennchen/zipstream-php": "^2.1 || ^3.0", + "markbaker/complex": "^3.0", + "markbaker/matrix": "^3.0", + "php": "^8.2", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "dev-main", + "dompdf/dompdf": "^2.0 || ^3.0", + "ext-intl": "*", + "friendsofphp/php-cs-fixer": "^3.2", + "mitoteam/jpgraph": "^10.5", + "mpdf/mpdf": "^8.1.1", + "phpcompatibility/php-compatibility": "^9.3", + "phpstan/phpstan": "^1.1 || ^2.0", + "phpstan/phpstan-deprecation-rules": "^1.0 || ^2.0", + "phpstan/phpstan-phpunit": "^1.0 || ^2.0", + "phpunit/phpunit": "^10.5 || ^11.0", + "squizlabs/php_codesniffer": "^3.7", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Option for rendering PDF with PDF Writer", + "ext-intl": "PHP Internationalization Functions, required for NumberFormat Wizard and StringHelper::setLocale()", + "mitoteam/jpgraph": "Option for rendering charts, or including charts with PDF or HTML Writers", + "mpdf/mpdf": "Option for rendering PDF with PDF Writer", + "tecnickcom/tcpdf": "Option for rendering PDF with PDF Writer" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpSpreadsheet\\": "src/PhpSpreadsheet" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "https://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker", + "homepage": "https://markbakeruk.net" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net" + }, + { + "name": "Erik Tilt" + }, + { + "name": "Adrien Crivelli" + }, + { + "name": "Owen Leibman" + } + ], + "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "https://github.com/PHPOffice/PhpSpreadsheet", + "keywords": [ + "OpenXML", + "excel", + "gnumeric", + "ods", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "support": { + "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/5.9.0" + }, + "time": "2026-07-12T19:17:39+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.5", diff --git a/resources/js/pages/admin/users/lecturers/index.tsx b/resources/js/pages/admin/users/lecturers/index.tsx index 109d298..c20455d 100644 --- a/resources/js/pages/admin/users/lecturers/index.tsx +++ b/resources/js/pages/admin/users/lecturers/index.tsx @@ -1,5 +1,5 @@ import { Head, router } from '@inertiajs/react'; -import { Plus, RotateCcw } from 'lucide-react'; +import { FileSpreadsheet, Plus, RotateCcw } from 'lucide-react'; import { useState } from 'react'; import { ConfirmDialog } from '@/components/confirm-dialog'; import type { PaginationState } from '@/components/data-table'; @@ -16,6 +16,7 @@ import { edit, destroy, reset_password, + exportMethod as exportLecturers, } from '@/routes/admin/users/lecturers'; import type { Lecturer } from './columns'; import { createLecturerColumns } from './columns'; @@ -123,12 +124,24 @@ export default function LecturerIndex({ - - - Tambah - - +
+ + +
} /> diff --git a/resources/js/pages/admin/users/students/index.tsx b/resources/js/pages/admin/users/students/index.tsx index f91ba64..ff799db 100644 --- a/resources/js/pages/admin/users/students/index.tsx +++ b/resources/js/pages/admin/users/students/index.tsx @@ -1,5 +1,5 @@ import { Head, router } from '@inertiajs/react'; -import { Info, Plus, RotateCcw } from 'lucide-react'; +import { FileSpreadsheet, Info, Plus, RotateCcw } from 'lucide-react'; import { useState } from 'react'; import { ConfirmDialog } from '@/components/confirm-dialog'; import type { PaginationState } from '@/components/data-table'; @@ -17,6 +17,7 @@ import { edit, reset_password, update_status, + exportMethod as exportStudents, index as studentsIndex, } from '@/routes/admin/users/students'; import { createStudentColumns } from './columns'; @@ -144,12 +145,24 @@ export default function StudentIndex({ - - - Tambah - - +
+ + +
} /> diff --git a/routes/admin.php b/routes/admin.php index 298e8f4..d739cf7 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -119,10 +119,12 @@ Route::prefix('admin/users')->name('admin.users.')->group(function () { Route::resource('lecturers', LecturerController::class)->except(['show'])->parameters(['lecturers' => 'user']); Route::patch('lecturers/{user}/reset-password', [LecturerController::class, 'resetPassword'])->name('lecturers.reset_password'); + Route::get('lecturers/export', [LecturerController::class, 'export'])->name('lecturers.export'); Route::resource('students', StudentController::class)->except(['show'])->parameters(['students' => 'user']); Route::patch('students/{user}/reset-password', [StudentController::class, 'resetPassword'])->name('students.reset_password'); Route::patch('students/{user}/status', [StudentController::class, 'updateStatus'])->name('students.update_status'); + Route::get('students/export', [StudentController::class, 'export'])->name('students.export'); Route::resource('administrators', AdministratorController::class)->except(['show'])->parameters(['administrators' => 'user']); Route::patch('administrators/{user}/reset-password', [AdministratorController::class, 'resetPassword'])->name('administrators.reset_password');