diff --git a/.dockerignore b/.dockerignore index 05391ae..984adce 100644 --- a/.dockerignore +++ b/.dockerignore @@ -23,3 +23,13 @@ files files/** !files/.gitkeep img/curiga +rebuild/.env +rebuild/.env.* +!rebuild/.env.example +rebuild/node_modules +rebuild/vendor +rebuild/storage/logs/* +rebuild/storage/framework/cache/* +rebuild/storage/framework/sessions/* +rebuild/storage/framework/views/* +rebuild/bootstrap/cache/*.php diff --git a/Dockerfile.rebuild b/Dockerfile.rebuild new file mode 100644 index 0000000..604cca9 --- /dev/null +++ b/Dockerfile.rebuild @@ -0,0 +1,48 @@ +FROM node:22-bookworm AS assets + +WORKDIR /app +COPY rebuild/package*.json ./ +RUN npm ci --ignore-scripts +COPY rebuild/ ./ +RUN npm run build + +FROM composer:2 AS vendor + +WORKDIR /app +COPY rebuild/composer.json rebuild/composer.lock ./ +RUN composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader --no-scripts +COPY rebuild/ ./ +RUN composer dump-autoload --optimize --no-scripts + +FROM php:8.4-apache + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libonig-dev \ + libpng-dev \ + libzip-dev \ + unzip \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && docker-php-ext-install -j"$(nproc)" pdo_mysql mysqli gd zip mbstring exif \ + && a2enmod rewrite headers \ + && rm -rf /var/lib/apt/lists/* + +COPY docker/php.ini /usr/local/etc/php/conf.d/spota.ini +COPY docker/apache-laravel-vhost.conf /etc/apache2/sites-available/000-default.conf + +WORKDIR /var/www/html +COPY rebuild/ /var/www/html +COPY --from=vendor /app/vendor /var/www/html/vendor +COPY --from=assets /app/public/build /var/www/html/public/build + +COPY docker/laravel-entrypoint.sh /usr/local/bin/laravel-entrypoint +RUN chmod +x /usr/local/bin/laravel-entrypoint \ + && mkdir -p storage/app storage/framework/cache storage/framework/sessions storage/framework/views storage/logs bootstrap/cache public/build \ + && rm -f bootstrap/cache/*.php \ + && APP_ENV=production APP_DEBUG=false APP_KEY=base64:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=spota_spotadb DB_USERNAME=spota_user DB_PASSWORD=spota_password SESSION_DRIVER=file CACHE_STORE=file QUEUE_CONNECTION=sync php artisan package:discover --ansi \ + && chown -R www-data:www-data storage bootstrap/cache public/build + +ENTRYPOINT ["laravel-entrypoint"] +CMD ["apache2-foreground"] diff --git a/docker-compose.coolify.yml b/docker-compose.coolify.yml index 36d2661..b28c766 100644 --- a/docker-compose.coolify.yml +++ b/docker-compose.coolify.yml @@ -2,13 +2,17 @@ services: app: build: context: . - dockerfile: Dockerfile.coolify + dockerfile: Dockerfile.rebuild restart: unless-stopped environment: - APP_URL: ${APP_URL} + APP_URL: ${APP_URL:-http://localhost} + APP_ENV: ${APP_ENV:-production} + APP_DEBUG: ${APP_DEBUG:-false} + APP_KEY: ${APP_KEY:-} DB_HOST: db - DB_USER: ${DB_USER} - DB_PASSWORD: ${DB_PASSWORD} + DB_PORT: 3306 + DB_USER: ${DB_USER:-spota_user} + DB_PASSWORD: ${DB_PASSWORD:-spota_password} DB_NAME: ${DB_NAME:-spota_spotadb} DB_SPOTA: ${DB_SPOTA:-spota_spotadb} DB_KONSULTASI: ${DB_KONSULTASI:-spota_konsultasi} @@ -16,10 +20,9 @@ services: DB_DOSEN: ${DB_DOSEN:-spota_spotadb} SERVICE_DB_NAME: ${SERVICE_DB_NAME:-spota_spotadb} PHP_DISPLAY_ERRORS: ${PHP_DISPLAY_ERRORS:-0} - FILES_STORAGE_PATH: ${FILES_STORAGE_PATH:-/var/www/html/files} + FILES_STORAGE_PATH: ${FILES_STORAGE_PATH:-/var/www/html/storage/app/files} volumes: - - spota_files:/var/www/html/files - - spota_img:/var/www/html/img + - spota_storage:/var/www/html/storage depends_on: db: condition: service_healthy @@ -31,9 +34,9 @@ services: restart: unless-stopped command: --default-authentication-plugin=mysql_native_password --character-set-server=latin1 --collation-server=latin1_swedish_ci --sql-mode=NO_ENGINE_SUBSTITUTION environment: - MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} - MYSQL_USER: ${DB_USER} - MYSQL_PASSWORD: ${DB_PASSWORD} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root_password} + MYSQL_USER: ${DB_USER:-spota_user} + MYSQL_PASSWORD: ${DB_PASSWORD:-spota_password} MYSQL_DATABASE: ${DB_NAME:-spota_spotadb} volumes: - spota_db_data:/var/lib/mysql @@ -45,5 +48,4 @@ services: volumes: spota_db_data: - spota_files: - spota_img: + spota_storage: diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 3344029..77d4ec1 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -3,8 +3,7 @@ services: ports: - "${APP_PORT:-8080}:80" volumes: - - ./files:/var/www/html/files - - ./img:/var/www/html/img + - ./rebuild/storage:/var/www/html/storage db: ports: diff --git a/docker-compose.yml b/docker-compose.yml index 139e0c7..175b111 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,13 +1,19 @@ services: app: - build: . + build: + context: . + dockerfile: Dockerfile.rebuild container_name: spota-app restart: unless-stopped expose: - "80" environment: APP_URL: ${APP_URL:-http://localhost} + APP_ENV: ${APP_ENV:-production} + APP_DEBUG: ${APP_DEBUG:-false} + APP_KEY: ${APP_KEY:-} DB_HOST: ${DB_HOST:-db} + DB_PORT: ${DB_PORT:-3306} DB_USER: ${DB_USER:-spota_user} DB_PASSWORD: ${DB_PASSWORD:-spota_password} DB_NAME: ${DB_NAME:-spota_spotadb} @@ -17,10 +23,9 @@ services: DB_DOSEN: ${DB_DOSEN:-spota_spotadb} SERVICE_DB_NAME: ${SERVICE_DB_NAME:-spota_spotadb} PHP_DISPLAY_ERRORS: ${PHP_DISPLAY_ERRORS:-1} - FILES_STORAGE_PATH: ${FILES_STORAGE_PATH:-/var/www/html/files} + FILES_STORAGE_PATH: ${FILES_STORAGE_PATH:-/var/www/html/storage/app/files} volumes: - - spota_files:/var/www/html/files - - spota_img:/var/www/html/img + - spota_storage:/var/www/html/storage depends_on: db: condition: service_healthy @@ -47,5 +52,4 @@ services: volumes: spota_db_data: - spota_files: - spota_img: + spota_storage: diff --git a/docker/apache-laravel-vhost.conf b/docker/apache-laravel-vhost.conf new file mode 100644 index 0000000..bb133bd --- /dev/null +++ b/docker/apache-laravel-vhost.conf @@ -0,0 +1,13 @@ + + ServerName localhost + DocumentRoot /var/www/html/public + + + Options Indexes FollowSymLinks + AllowOverride All + Require all granted + + + ErrorLog ${APACHE_LOG_DIR}/error.log + CustomLog ${APACHE_LOG_DIR}/access.log combined + diff --git a/docker/laravel-entrypoint.sh b/docker/laravel-entrypoint.sh new file mode 100644 index 0000000..f3f3a1e --- /dev/null +++ b/docker/laravel-entrypoint.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env sh +set -e + +cd /var/www/html + +mkdir -p storage/app/files storage/framework/cache storage/framework/sessions storage/framework/views storage/logs bootstrap/cache public/build + +if [ ! -f .env ]; then + cat > .env </dev/null || true + +exec "$@" diff --git a/link6.jpg b/link6.jpg new file mode 100644 index 0000000..ef16de7 Binary files /dev/null and b/link6.jpg differ diff --git a/rebuild/.editorconfig b/rebuild/.editorconfig new file mode 100644 index 0000000..6df8428 --- /dev/null +++ b/rebuild/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[{compose,docker-compose}.{yml,yaml}] +indent_size = 4 diff --git a/rebuild/.env.example b/rebuild/.env.example new file mode 100644 index 0000000..c0660ea --- /dev/null +++ b/rebuild/.env.example @@ -0,0 +1,65 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_URL=http://localhost + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US + +APP_MAINTENANCE_DRIVER=file +# APP_MAINTENANCE_STORE=database + +# PHP_CLI_SERVER_WORKERS=4 + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=laravel +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +# CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_SCHEME=null +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" diff --git a/rebuild/.gitattributes b/rebuild/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/rebuild/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/rebuild/.gitignore b/rebuild/.gitignore new file mode 100644 index 0000000..83d894f --- /dev/null +++ b/rebuild/.gitignore @@ -0,0 +1,26 @@ +*.log +.DS_Store +.env +.env.backup +.env.production +.phpactor.json +.phpunit.result.cache +/.codex +/.cursor/ +/.idea +/.nova +/.phpunit.cache +/.vscode +/.zed +/auth.json +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/storage/pail +/vendor +_ide_helper.php +Homestead.json +Homestead.yaml +Thumbs.db diff --git a/rebuild/.npmrc b/rebuild/.npmrc new file mode 100644 index 0000000..495a6af --- /dev/null +++ b/rebuild/.npmrc @@ -0,0 +1,2 @@ +ignore-scripts=true +audit=true diff --git a/rebuild/README.md b/rebuild/README.md new file mode 100644 index 0000000..8574e3f --- /dev/null +++ b/rebuild/README.md @@ -0,0 +1,70 @@ +# SPOTA Rebuild + +Laravel Blade + Tailwind prototype untuk rebuild penuh antarmuka SPOTA. + +## Current scope + +- Landing page dan gateway masuk untuk mahasiswa, dosen, dan admin +- Auth rebuild yang membaca tabel legacy `tbmhs`, `tbdosen`, dan `tbadmin` +- Dashboard mahasiswa, dosen, dan admin berbasis data legacy +- Modul native awal untuk praoutline, pengumuman, penawaran judul, early warning, dan CRUD admin prioritas +- Fondasi Laravel terpisah dari aplikasi legacy supaya migrasi bisa bertahap + +## Run locally + +```powershell +copy .env.example .env +php artisan serve +npm run dev +``` + +Atau build asset production: + +```powershell +npm run build +``` + +## Docker and Coolify + +Compose utama dan compose Coolify sekarang menjalankan Laravel rebuild dari `Dockerfile.rebuild` dengan document root Apache ke `rebuild/public`. + +Untuk Coolify, gunakan compose file: + +```text +docker-compose.coolify.yml +``` + +Environment yang sebaiknya diisi di Coolify: + +```dotenv +APP_URL=https://domain-spota.example +APP_ENV=production +APP_DEBUG=false +APP_KEY=base64:isi_dengan_key_production +DB_USER=spota_user +DB_PASSWORD=spota_password +MYSQL_ROOT_PASSWORD=root_password_yang_kuat +DB_NAME=spota_spotadb +``` + +Jika `APP_KEY` kosong, container akan generate key otomatis saat start. Untuk production lebih aman isi `APP_KEY` permanen supaya session tidak berubah antar redeploy. + +## Legacy database setup + +Ubah `.env` agar rebuild memakai database SPOTA legacy, misalnya: + +```dotenv +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3307 +DB_DATABASE=spota_spotadb +DB_USERNAME=spota_user +DB_PASSWORD=spota_password +SESSION_DRIVER=file +``` + +Catatan: + +- Dashboard mahasiswa membaca data real dari `tbmhs`, `tbpraoutline`, `tbrekaphasil`, `tbpengumuman`, `tmp_notif`, dan `tbjadwal` +- Dashboard dosen membaca data real dari modul praoutline, pengumuman, jadwal, penawaran judul, dan early warning +- Dashboard admin membaca data real dari master data, praoutline, pengumuman, jadwal, dan pengaturan prodi diff --git a/rebuild/app/Http/Controllers/AdminLegacyController.php b/rebuild/app/Http/Controllers/AdminLegacyController.php new file mode 100644 index 0000000..8d25ca2 --- /dev/null +++ b/rebuild/app/Http/Controllers/AdminLegacyController.php @@ -0,0 +1,25 @@ +session()->get('legacy_auth'); + + abort_unless(($auth['role'] ?? null) === 'admin', 403); + + $query = $request->query(); + $legacyUrl = 'http://127.0.0.1:8080/admin/dashboard.php'; + + if (! empty($query)) { + $legacyUrl .= '?'.http_build_query($query); + } + + return redirect()->away($legacyUrl); + } +} diff --git a/rebuild/app/Http/Controllers/AdminPageController.php b/rebuild/app/Http/Controllers/AdminPageController.php new file mode 100644 index 0000000..8305c48 --- /dev/null +++ b/rebuild/app/Http/Controllers/AdminPageController.php @@ -0,0 +1,801 @@ +user($request); + $this->abortIfSuper($user); + + $rows = DB::table('tbmhs as tm') + ->leftJoin('tbprodi as tp', 'tm.idProdi', '=', 'tp.idProdi') + ->select(['tm.idmhs', 'tm.nim', 'tm.nmLengkap', 'tp.nmProdi', 'tm.thnmasuk', 'tm.status']) + ->where('tm.idProdi', $user['prodi']) + ->orderByDesc('tm.idmhs') + ->paginate(20); + + return $this->table($request, 'Data Mahasiswa', 'Data mahasiswa pada program studi admin aktif.', ['NIM', 'Nama', 'Prodi', 'Angkatan', 'Status'], $rows, fn ($row) => [$row->nim, $row->nmLengkap, $row->nmProdi, $row->thnmasuk ?: '-', $row->status], 'admin.data.mahasiswa', [ + 'actions' => [ + ['label' => 'Tambah Mahasiswa', 'href' => route('admin.data.mahasiswa.create', [], false), 'variant' => 'dark'], + ['label' => 'Impor CSV Mahasiswa', 'href' => route('admin.data.mahasiswa.import', [], false), 'variant' => 'dark'], + ], + 'rowActions' => fn ($row) => [ + ['label' => 'Edit', 'href' => route('admin.data.mahasiswa.edit', ['id' => $row->idmhs], false)], + ['label' => 'Hapus', 'href' => route('admin.data.mahasiswa.destroy', ['id' => $row->idmhs], false), 'method' => 'DELETE', 'confirm' => 'Hapus mahasiswa ini?'], + ], + ]); + } + + public function createMahasiswa(Request $request): View + { + return $this->resourceForm($request, 'Tambah Mahasiswa', 'Tambah data mahasiswa pada program studi admin.', route('admin.data.mahasiswa.store'), route('admin.data.mahasiswa', [], false), $this->mahasiswaFields()); + } + + public function storeMahasiswa(Request $request): RedirectResponse + { + $user = $this->user($request); + $data = $request->validate([ + 'nim' => ['required', 'string', 'max:30'], + 'nmLengkap' => ['required', 'string', 'max:255'], + 'email' => ['nullable', 'email', 'max:255'], + 'thnmasuk' => ['nullable', 'string', 'max:10'], + 'password' => ['required', 'string', 'min:4'], + 'status' => ['required', 'in:A,N,P'], + 'bolehUploadDraft' => ['required', 'in:0,1'], + 'noHP' => ['nullable', 'string', 'max:30'], + 'noHPOrtu' => ['nullable', 'string', 'max:30'], + ]); + + $data['idProdi'] = $user['prodi']; + $data['password'] = md5($data['password']); + + DB::table('tbmhs')->insert($data); + + return redirect()->route('admin.data.mahasiswa')->with('success', 'Data mahasiswa berhasil ditambahkan.'); + } + + public function editMahasiswa(Request $request, int $id): View + { + $user = $this->user($request); + $row = DB::table('tbmhs')->where('idmhs', $id)->where('idProdi', $user['prodi'])->first(); + abort_unless($row, 404); + + return $this->resourceForm($request, 'Edit Mahasiswa', 'Perbarui data mahasiswa.', route('admin.data.mahasiswa.update', ['id' => $id]), route('admin.data.mahasiswa', [], false), $this->mahasiswaFields($row, true), 'PUT'); + } + + public function updateMahasiswa(Request $request, int $id): RedirectResponse + { + $user = $this->user($request); + abort_unless(DB::table('tbmhs')->where('idmhs', $id)->where('idProdi', $user['prodi'])->exists(), 404); + $data = $request->validate([ + 'nim' => ['required', 'string', 'max:30'], + 'nmLengkap' => ['required', 'string', 'max:255'], + 'email' => ['nullable', 'email', 'max:255'], + 'thnmasuk' => ['nullable', 'string', 'max:10'], + 'password' => ['nullable', 'string', 'min:4'], + 'status' => ['required', 'in:A,N,P'], + 'bolehUploadDraft' => ['required', 'in:0,1'], + 'noHP' => ['nullable', 'string', 'max:30'], + 'noHPOrtu' => ['nullable', 'string', 'max:30'], + ]); + if (($data['password'] ?? '') !== '') { + $data['password'] = md5($data['password']); + } else { + unset($data['password']); + } + DB::table('tbmhs')->where('idmhs', $id)->update($data); + + return redirect()->route('admin.data.mahasiswa')->with('success', 'Data mahasiswa berhasil diperbarui.'); + } + + public function destroyMahasiswa(Request $request, int $id): RedirectResponse + { + $user = $this->user($request); + DB::table('tbmhs')->where('idmhs', $id)->where('idProdi', $user['prodi'])->delete(); + + return redirect()->route('admin.data.mahasiswa')->with('success', 'Data mahasiswa berhasil dihapus.'); + } + + public function importMahasiswa(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + return view('admin.pages.mahasiswa-import', [ + 'title' => 'Impor CSV Mahasiswa | SPOTA Rebuild', + 'pageTitle' => 'Impor CSV Mahasiswa', + 'pageDescription' => 'Upload CSV untuk menambah atau memperbarui data mahasiswa secara bulk berdasarkan NIM.', + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => AdminNavigation::build($user), + 'user' => $user, + 'requiredHeaders' => ['nim', 'nama', 'email', 'thnmasuk'], + 'optionalHeaders' => ['password', 'status', 'noHP', 'noHPOrtu', 'bolehUploadDraft'], + ]); + } + + public function storeImportMahasiswa(Request $request): RedirectResponse + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $validated = $request->validate([ + 'csv' => ['required', 'file', 'mimes:csv,txt', 'max:5120'], + 'mode' => ['required', 'in:insert_only,upsert'], + 'default_password' => ['nullable', 'string', 'min:4', 'max:50'], + ]); + + $path = $request->file('csv')->getRealPath(); + $handle = fopen($path, 'r'); + + if (! $handle) { + return back()->with('error', 'File CSV tidak dapat dibaca.'); + } + + $headers = fgetcsv($handle); + + if (! $headers) { + fclose($handle); + + return back()->with('error', 'CSV kosong atau header tidak ditemukan.'); + } + + $headers = array_map(fn ($header) => trim((string) $header), $headers); + $normalized = array_map(fn ($header) => strtolower($header), $headers); + $aliases = [ + 'nama_lengkap' => 'nama', + 'nmlengkap' => 'nama', + 'tahunmasuk' => 'thnmasuk', + 'tahun_masuk' => 'thnmasuk', + 'nohp' => 'noHP', + 'no_hp' => 'noHP', + 'nohportu' => 'noHPOrtu', + 'no_hp_ortu' => 'noHPOrtu', + 'bolehuploaddraft' => 'bolehUploadDraft', + 'boleh_upload_draft' => 'bolehUploadDraft', + ]; + + $canonical = []; + foreach ($normalized as $index => $header) { + $canonical[$index] = $aliases[$header] ?? $headers[$index]; + } + + $required = ['nim', 'nama', 'email', 'thnmasuk']; + $missing = array_diff($required, $canonical); + + if (! empty($missing)) { + fclose($handle); + + return back()->with('error', 'Header CSV wajib belum lengkap: '.implode(', ', $missing).'.'); + } + + $inserted = 0; + $updated = 0; + $skipped = 0; + $errors = []; + $line = 1; + $defaultPassword = $validated['default_password'] ?: '123456'; + + DB::beginTransaction(); + + try { + while (($row = fgetcsv($handle)) !== false) { + $line++; + $data = []; + + foreach ($canonical as $index => $header) { + $data[$header] = trim((string) ($row[$index] ?? '')); + } + + if (count(array_filter($data, fn ($value) => $value !== '')) === 0) { + continue; + } + + $nim = $data['nim'] ?? ''; + $nama = $data['nama'] ?? ''; + $email = $data['email'] ?? ''; + $thnmasuk = $data['thnmasuk'] ?? ''; + + if ($nim === '' || $nama === '' || $email === '' || $thnmasuk === '') { + $skipped++; + $errors[] = 'Baris '.$line.': nim, nama, email, dan thnmasuk wajib diisi.'; + continue; + } + + if (! preg_match('/^[A-Za-z0-9._-]+$/', $nim)) { + $skipped++; + $errors[] = 'Baris '.$line.': NIM tidak valid.'; + continue; + } + + if (! filter_var($email, FILTER_VALIDATE_EMAIL)) { + $skipped++; + $errors[] = 'Baris '.$line.': email tidak valid.'; + continue; + } + + $payload = [ + 'nim' => $nim, + 'nmLengkap' => $nama, + 'email' => $email, + 'idProdi' => $user['prodi'], + 'thnmasuk' => $thnmasuk, + 'status' => ($data['status'] ?? '') !== '' ? $data['status'] : 'A', + 'noHP' => $data['noHP'] ?? '', + 'noHPOrtu' => $data['noHPOrtu'] ?? '', + 'bolehUploadDraft' => ($data['bolehUploadDraft'] ?? '') !== '' ? $data['bolehUploadDraft'] : '1', + ]; + + $existing = DB::table('tbmhs') + ->where('nim', $nim) + ->where('idProdi', $user['prodi']) + ->first(); + + if ($existing && $validated['mode'] === 'insert_only') { + $skipped++; + continue; + } + + if ($existing) { + if (($data['password'] ?? '') !== '') { + $payload['password'] = md5($data['password']); + } + + DB::table('tbmhs')->where('idmhs', $existing->idmhs)->update($payload); + $updated++; + } else { + $payload['password'] = md5(($data['password'] ?? '') !== '' ? $data['password'] : $defaultPassword); + DB::table('tbmhs')->insert($payload); + $inserted++; + } + } + + DB::commit(); + } catch (\Throwable $exception) { + DB::rollBack(); + fclose($handle); + + return back()->with('error', 'Import gagal: '.$exception->getMessage()); + } + + fclose($handle); + + return redirect() + ->route('admin.data.mahasiswa') + ->with('success', 'Import selesai. Baru: '.$inserted.', diperbarui: '.$updated.', dilewati: '.$skipped.'.'.($errors ? ' Catatan: '.implode(' ', array_slice($errors, 0, 5)) : '')); + } + + public function dosen(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $rows = DB::table('tbdosen as td') + ->leftJoin('tbprodi as tp', 'td.idProdi', '=', 'tp.idProdi') + ->select(['td.iddosen', 'td.nip', 'td.nmLengkap', 'td.email', 'tp.nmProdi', 'td.status']) + ->where('td.idProdi', $user['prodi']) + ->orderBy('td.nmLengkap') + ->paginate(20); + + return $this->table($request, 'Data Dosen', 'Data dosen pada program studi admin aktif.', ['NIP', 'Nama', 'Email', 'Prodi', 'Status'], $rows, fn ($row) => [$row->nip, $row->nmLengkap, $row->email ?: '-', $row->nmProdi, $row->status], 'admin.data.dosen', [ + 'actions' => [ + ['label' => 'Tambah Dosen', 'href' => route('admin.data.dosen.create', [], false), 'variant' => 'dark'], + ], + 'rowActions' => fn ($row) => [ + ['label' => 'Edit', 'href' => route('admin.data.dosen.edit', ['id' => $row->iddosen], false)], + ['label' => 'Hapus', 'href' => route('admin.data.dosen.destroy', ['id' => $row->iddosen], false), 'method' => 'DELETE', 'confirm' => 'Hapus dosen ini?'], + ], + ]); + } + + public function createDosen(Request $request): View + { + return $this->resourceForm($request, 'Tambah Dosen', 'Tambah data dosen pada program studi admin.', route('admin.data.dosen.store'), route('admin.data.dosen', [], false), $this->dosenFields()); + } + + public function storeDosen(Request $request): RedirectResponse + { + $user = $this->user($request); + $data = $request->validate([ + 'nip' => ['required', 'string', 'max:30'], + 'nmLengkap' => ['required', 'string', 'max:255'], + 'email' => ['nullable', 'email', 'max:255'], + 'nohp' => ['nullable', 'string', 'max:30'], + 'jenis' => ['required', 'string', 'max:10'], + 'password' => ['required', 'string', 'min:4'], + 'status' => ['required', 'in:A,N,P'], + ]); + + $data['idProdi'] = $user['prodi']; + $data['password'] = md5($data['password']); + + DB::table('tbdosen')->insert($data); + + return redirect()->route('admin.data.dosen')->with('success', 'Data dosen berhasil ditambahkan.'); + } + + public function editDosen(Request $request, int $id): View + { + $user = $this->user($request); + $row = DB::table('tbdosen')->where('iddosen', $id)->where('idProdi', $user['prodi'])->first(); + abort_unless($row, 404); + + return $this->resourceForm($request, 'Edit Dosen', 'Perbarui data dosen.', route('admin.data.dosen.update', ['id' => $id]), route('admin.data.dosen', [], false), $this->dosenFields($row, true), 'PUT'); + } + + public function updateDosen(Request $request, int $id): RedirectResponse + { + $user = $this->user($request); + abort_unless(DB::table('tbdosen')->where('iddosen', $id)->where('idProdi', $user['prodi'])->exists(), 404); + $data = $request->validate([ + 'nip' => ['required', 'string', 'max:30'], + 'nmLengkap' => ['required', 'string', 'max:255'], + 'email' => ['nullable', 'email', 'max:255'], + 'nohp' => ['nullable', 'string', 'max:30'], + 'jenis' => ['required', 'string', 'max:10'], + 'password' => ['nullable', 'string', 'min:4'], + 'status' => ['required', 'in:A,N,P'], + ]); + if (($data['password'] ?? '') !== '') { + $data['password'] = md5($data['password']); + } else { + unset($data['password']); + } + DB::table('tbdosen')->where('iddosen', $id)->update($data); + + return redirect()->route('admin.data.dosen')->with('success', 'Data dosen berhasil diperbarui.'); + } + + public function destroyDosen(Request $request, int $id): RedirectResponse + { + $user = $this->user($request); + DB::table('tbdosen')->where('iddosen', $id)->where('idProdi', $user['prodi'])->delete(); + + return redirect()->route('admin.data.dosen')->with('success', 'Data dosen berhasil dihapus.'); + } + + public function kk(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $rows = DB::table('tb_kelompok_keahlian as kk') + ->leftJoin('tbdosen as ketua', 'kk.ketuaKK', '=', 'ketua.iddosen') + ->leftJoin('tbdosen as sekretaris', 'kk.sekretarisKK', '=', 'sekretaris.iddosen') + ->select(['kk.idKK', 'kk.namaKK', 'kk.warnaLabel', 'ketua.nmLengkap as ketua', 'sekretaris.nmLengkap as sekretaris']) + ->orderBy('kk.namaKK') + ->paginate(20); + + return $this->table($request, 'Data Kelompok Keahlian', 'Daftar kelompok keahlian yang dipakai pada praoutline dan penawaran judul.', ['ID', 'Nama KK', 'Label', 'Ketua', 'Sekretaris'], $rows, fn ($row) => [$row->idKK, $row->namaKK, $row->warnaLabel ?: '-', $row->ketua ?: '-', $row->sekretaris ?: '-'], 'admin.data.kk', [ + 'actions' => [ + ['label' => 'Tambah KK', 'href' => route('admin.data.kk.create', [], false), 'variant' => 'dark'], + ], + 'rowActions' => fn ($row) => [ + ['label' => 'Edit', 'href' => route('admin.data.kk.edit', ['id' => $row->idKK], false)], + ['label' => 'Hapus', 'href' => route('admin.data.kk.destroy', ['id' => $row->idKK], false), 'method' => 'DELETE', 'confirm' => 'Hapus kelompok keahlian ini?'], + ], + ]); + } + + public function createKk(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + return $this->resourceForm($request, 'Tambah Kelompok Keahlian', 'Tambah data kelompok keahlian.', route('admin.data.kk.store'), route('admin.data.kk', [], false), $this->kkFields()); + } + + public function storeKk(Request $request): RedirectResponse + { + $user = $this->user($request); + $this->abortIfSuper($user); + $data = $request->validate([ + 'namaKK' => ['required', 'string', 'max:255'], + 'warnaLabel' => ['nullable', 'string', 'max:50'], + 'ketuaKK' => ['nullable', 'integer'], + 'sekretarisKK' => ['nullable', 'integer'], + ]); + DB::table('tb_kelompok_keahlian')->insert($data); + + return redirect()->route('admin.data.kk')->with('success', 'Data KK berhasil ditambahkan.'); + } + + public function editKk(Request $request, int $id): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + $row = DB::table('tb_kelompok_keahlian')->where('idKK', $id)->first(); + abort_unless($row, 404); + + return $this->resourceForm($request, 'Edit Kelompok Keahlian', 'Perbarui data kelompok keahlian.', route('admin.data.kk.update', ['id' => $id]), route('admin.data.kk', [], false), $this->kkFields($row), 'PUT'); + } + + public function updateKk(Request $request, int $id): RedirectResponse + { + $user = $this->user($request); + $this->abortIfSuper($user); + $data = $request->validate([ + 'namaKK' => ['required', 'string', 'max:255'], + 'warnaLabel' => ['nullable', 'string', 'max:50'], + 'ketuaKK' => ['nullable', 'integer'], + 'sekretarisKK' => ['nullable', 'integer'], + ]); + DB::table('tb_kelompok_keahlian')->where('idKK', $id)->update($data); + + return redirect()->route('admin.data.kk')->with('success', 'Data KK berhasil diperbarui.'); + } + + public function destroyKk(Request $request, int $id): RedirectResponse + { + $user = $this->user($request); + $this->abortIfSuper($user); + DB::table('tb_kelompok_keahlian')->where('idKK', $id)->delete(); + + return redirect()->route('admin.data.kk')->with('success', 'Data KK berhasil dihapus.'); + } + + public function fakultas(Request $request): View + { + $user = $this->user($request); + $this->abortUnlessSuper($user); + + $rows = DB::table('tbfakultas')->select(['idFak', 'nmFakultas'])->orderBy('nmFakultas')->paginate(20); + + return $this->table($request, 'Data Fakultas', 'Data fakultas untuk superadmin.', ['ID', 'Nama Fakultas'], $rows, fn ($row) => [$row->idFak, $row->nmFakultas], 'admin.data.fakultas'); + } + + public function jurusan(Request $request): View + { + $user = $this->user($request); + $this->abortUnlessSuper($user); + + $rows = DB::table('tbjurusan as tj') + ->leftJoin('tbfakultas as tf', 'tj.idFak', '=', 'tf.idFak') + ->select(['tj.idJur', 'tj.nmJurusan', 'tf.nmFakultas']) + ->orderBy('tj.nmJurusan') + ->paginate(20); + + return $this->table($request, 'Data Jurusan', 'Data jurusan untuk superadmin.', ['ID', 'Jurusan', 'Fakultas'], $rows, fn ($row) => [$row->idJur, $row->nmJurusan, $row->nmFakultas ?: '-'], 'admin.data.jurusan'); + } + + public function prodi(Request $request): View + { + $user = $this->user($request); + $this->abortUnlessSuper($user); + + $rows = DB::table('tbprodi as tp') + ->leftJoin('tbjurusan as tj', 'tp.idJur', '=', 'tj.idJur') + ->leftJoin('tbfakultas as tf', 'tp.idFak', '=', 'tf.idFak') + ->select(['tp.idProdi', 'tp.nmProdi', 'tj.nmJurusan', 'tf.nmFakultas']) + ->orderBy('tp.nmProdi') + ->paginate(20); + + return $this->table($request, 'Data Program Studi', 'Data program studi untuk superadmin.', ['ID', 'Program Studi', 'Jurusan', 'Fakultas'], $rows, fn ($row) => [$row->idProdi, $row->nmProdi, $row->nmJurusan ?: '-', $row->nmFakultas ?: '-'], 'admin.data.prodi'); + } + + public function praoutline(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $rows = $this->praoutlineBase($user) + ->orderByDesc('tp.id') + ->paginate(20); + + return $this->table($request, 'Daftar Draft Praoutline', 'Draft praoutline terbaru pada program studi admin.', ['ID', 'Mahasiswa', 'Judul', 'Tanggal Upload', 'Status'], $rows, fn ($row) => [$row->id, $row->mahasiswa.' ('.$row->nim.')', $row->judul, $this->formatDateTime(trim($row->tgl_upload.' '.$row->wkt_upload)), $this->statusLabel($row->status_usulan)], 'admin.praoutline.index'); + } + + public function praoutlineSearch(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + $keyword = trim((string) $request->query('q')); + + $query = $this->praoutlineBase($user)->orderByDesc('tp.id'); + + if ($keyword !== '') { + $query->where(function ($query) use ($keyword) { + $query->where('tp.judul', 'like', '%'.$keyword.'%') + ->orWhere('tm.nmLengkap', 'like', '%'.$keyword.'%') + ->orWhere('tp.nim', 'like', '%'.$keyword.'%'); + }); + } + + $rows = $query->paginate(20)->withQueryString(); + + return $this->table($request, 'Pencarian Praoutline', 'Cari draft berdasarkan judul, nama mahasiswa, atau NIM.', ['ID', 'Mahasiswa', 'Judul', 'Tanggal Upload', 'Status'], $rows, fn ($row) => [$row->id, $row->mahasiswa.' ('.$row->nim.')', $row->judul, $this->formatDateTime(trim($row->tgl_upload.' '.$row->wkt_upload)), $this->statusLabel($row->status_usulan)], 'admin.praoutline.search', ['search' => true, 'keyword' => $keyword]); + } + + public function keputusan(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $rows = DB::table('tbrekaphasil as trh') + ->leftJoin('tbmhs as tm', 'trh.nim', '=', 'tm.nim') + ->leftJoin('tbpraoutline as tp', 'trh.idpraoutline', '=', 'tp.id') + ->select(['trh.id', 'trh.nim', 'tm.nmLengkap as mahasiswa', 'tp.judul', 'trh.kep_akhir', 'trh.tgl']) + ->where('trh.idProdi', $user['prodi']) + ->orderByDesc('trh.id') + ->paginate(20); + + return $this->table($request, 'Kep. Penunjukan Dosen', 'Rekap keputusan dan penunjukan dosen dari data praoutline.', ['ID', 'Mahasiswa', 'Judul', 'Keputusan', 'Tanggal'], $rows, fn ($row) => [$row->id, ($row->mahasiswa ?: '-').' ('.$row->nim.')', $row->judul ?: '-', $this->statusLabel($row->kep_akhir), $this->formatDateTime($row->tgl)], 'admin.praoutline.keputusan'); + } + + public function kepDraft(Request $request): View + { + return $this->keputusan($request)->with('pageTitle', 'Kep. Draft Praoutline'); + } + + public function pemberitahuan(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $rows = DB::table('tmp_notif_r as tn') + ->select(['tn.id', 'tn.msg', 'tn.tgl', 'tn.jns_usr', 'tn.idkonten', 'tn.read']) + ->where('tn.idProdi', $user['prodi']) + ->orderByDesc('tn.id') + ->paginate(20); + + return $this->table($request, 'Pemberitahuan', 'Pemberitahuan praoutline yang tercatat pada sistem.', ['ID', 'Isi', 'Jenis User', 'Konten', 'Status', 'Tanggal'], $rows, fn ($row) => [$row->id, $row->msg, $row->jns_usr, $row->idkonten, $row->read === 'Y' ? 'Dibaca' : 'Belum Dibaca', $this->formatDateTime($row->tgl)], 'admin.praoutline.pemberitahuan'); + } + + public function pengumuman(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $rows = DB::table('tbpengumuman') + ->select(['id', 'judul', 'tujuan', 'publish', 'tgl']) + ->where('idProdi', $user['prodi']) + ->orderByDesc('id') + ->paginate(20); + + return $this->table($request, 'Daftar Pengumuman', 'Pengumuman program studi yang ditampilkan di SPOTA.', ['ID', 'Judul', 'Tujuan', 'Publish', 'Tanggal'], $rows, fn ($row) => [$row->id, $row->judul, $row->tujuan, $row->publish, $this->formatDateTime($row->tgl)], 'admin.pengumuman.index', ['actions' => [['label' => 'Buat Pengumuman Baru', 'href' => route('admin.pengumuman.create', [], false), 'variant' => 'dark']]]); + } + + public function createPengumuman(Request $request): View + { + return $this->form($request, 'Buat Pengumuman Baru', 'Tambah pengumuman untuk mahasiswa, dosen, atau seluruh pengguna program studi.', 'admin.pengumuman.create'); + } + + public function storePengumuman(Request $request): RedirectResponse + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $validated = $request->validate([ + 'judul' => ['required', 'string', 'max:255'], + 'isi' => ['required', 'string'], + 'tujuan' => ['required', 'in:A,M,D'], + 'publish' => ['nullable', 'in:Y,N'], + ]); + + DB::table('tbpengumuman')->insert([ + 'idProdi' => $user['prodi'], + 'judul' => $validated['judul'], + 'isi' => $validated['isi'], + 'tujuan' => $validated['tujuan'], + 'tgl' => Carbon::now()->toDateTimeString(), + 'author' => $user['id'], + 'publish' => $validated['publish'] ?? 'Y', + ]); + + return redirect()->route('admin.pengumuman.index')->with('success', 'Pengumuman berhasil dibuat.'); + } + + public function jadwal(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $rows = DB::table('tbjadwal as tj') + ->leftJoin('tbmhs as tm', 'tj.idMhs', '=', 'tm.idmhs') + ->select(['tj.id', 'tj.jenis', 'tj.start', 'tj.ruangan', 'tj.publish', 'tm.nmLengkap as mahasiswa']) + ->where('tj.idProdi', $user['prodi']) + ->orderByDesc('tj.start') + ->paginate(20); + + return $this->table($request, 'Manajemen Data Jadwal', 'Data jadwal seminar/sidang program studi.', ['ID', 'Jenis', 'Mahasiswa', 'Tanggal', 'Ruangan', 'Publish'], $rows, fn ($row) => [$row->id, $row->jenis, $row->mahasiswa ?: '-', $this->formatDateTime($row->start), $row->ruangan ?: '-', $row->publish], 'admin.jadwal.index'); + } + + public function kalender(Request $request): View + { + return $this->jadwal($request)->with('pageTitle', 'Kalender Seminar/Sidang'); + } + + public function profile(Request $request): View + { + $user = $this->user($request); + + $rows = collect([(object) [ + 'username' => $user['username'], + 'nama' => $user['nama_lengkap'], + 'jabatan' => $user['jabatan'] ?: '-', + 'email' => $user['email'] ?: '-', + 'prodi' => $user['nmprodi'], + 'level' => $user['lvl'] === 'S' ? 'Super Admin' : 'Admin Prodi', + ]]); + + return $this->staticTable($request, 'Profil Saya', 'Profil admin yang sedang login.', ['Username', 'Nama', 'Jabatan', 'Email', 'Prodi', 'Level'], $rows, fn ($row) => [$row->username, $row->nama, $row->jabatan, $row->email, $row->prodi, $row->level], 'admin.profile'); + } + + public function users(Request $request): View + { + $user = $this->user($request); + $this->abortUnlessSuper($user); + + $rows = DB::table('tbadmin as ta') + ->leftJoin('tbprodi as tp', 'ta.idProdi', '=', 'tp.idProdi') + ->select(['ta.username', 'ta.nmLengkap', 'ta.jenisAdmin', 'ta.aktif', 'tp.nmProdi']) + ->orderBy('ta.username') + ->paginate(20); + + return $this->table($request, 'Manajemen Admin', 'Daftar akun administrator SPOTA.', ['Username', 'Nama', 'Level', 'Prodi', 'Aktif'], $rows, fn ($row) => [$row->username, $row->nmLengkap, $row->jenisAdmin, $row->nmProdi ?: 'Semua', $row->aktif], 'admin.users'); + } + + public function pengaturan(Request $request): View + { + $user = $this->user($request); + $this->abortIfSuper($user); + + $rows = DB::table('web_setting') + ->select(['name', 'values']) + ->where('idProdi', $user['prodi']) + ->orderBy('name') + ->paginate(20); + + return $this->table($request, 'Pengaturan Prodi', 'Pengaturan aktif program studi pada SPOTA.', ['Nama', 'Nilai'], $rows, fn ($row) => [$row->name, $row->values], 'admin.pengaturan'); + } + + private function table(Request $request, string $title, string $description, array $columns, mixed $rows, callable $map, string $activeRoute, array $extra = []): View + { + $user = $this->user($request); + + return view('admin.pages.table', array_merge([ + 'title' => $title.' | SPOTA Rebuild', + 'pageTitle' => $title, + 'pageDescription' => $description, + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => AdminNavigation::build($user), + 'user' => $user, + 'columns' => $columns, + 'rows' => $rows, + 'map' => $map, + ], $extra)); + } + + private function staticTable(Request $request, string $title, string $description, array $columns, mixed $rows, callable $map, string $activeRoute): View + { + return $this->table($request, $title, $description, $columns, $rows, $map, $activeRoute); + } + + private function form(Request $request, string $title, string $description, string $activeRoute): View + { + $user = $this->user($request); + + return view('admin.pages.pengumuman-form', [ + 'title' => $title.' | SPOTA Rebuild', + 'pageTitle' => $title, + 'pageDescription' => $description, + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => AdminNavigation::build($user), + 'user' => $user, + ]); + } + + private function resourceForm(Request $request, string $title, string $description, string $action, string $cancel, array $fields, string $method = 'POST'): View + { + $user = $this->user($request); + + return view('admin.pages.resource-form', [ + 'title' => $title.' | SPOTA Rebuild', + 'pageTitle' => $title, + 'pageDescription' => $description, + 'sidebar' => AdminNavigation::build($user), + 'user' => $user, + 'action' => $action, + 'cancel' => $cancel, + 'fields' => $fields, + 'method' => $method, + ]); + } + + private function mahasiswaFields(?object $row = null, bool $editing = false): array + { + return [ + ['name' => 'nim', 'label' => 'NIM', 'value' => $row->nim ?? '', 'required' => true], + ['name' => 'nmLengkap', 'label' => 'Nama Lengkap', 'value' => $row->nmLengkap ?? '', 'required' => true], + ['name' => 'email', 'label' => 'Email', 'type' => 'email', 'value' => $row->email ?? ''], + ['name' => 'thnmasuk', 'label' => 'Tahun Masuk', 'value' => $row->thnmasuk ?? ''], + ['name' => 'password', 'label' => 'Password', 'type' => 'password', 'required' => ! $editing, 'help' => $editing ? 'Kosongkan jika tidak mengganti password.' : 'Password awal mahasiswa.'], + ['name' => 'status', 'label' => 'Status', 'type' => 'select', 'value' => $row->status ?? 'A', 'options' => ['A' => 'Aktif', 'P' => 'Pending', 'N' => 'Nonaktif']], + ['name' => 'bolehUploadDraft', 'label' => 'Boleh Upload Draft', 'type' => 'select', 'value' => $row->bolehUploadDraft ?? '1', 'options' => ['1' => 'Ya', '0' => 'Tidak']], + ['name' => 'noHP', 'label' => 'No HP', 'value' => $row->noHP ?? ''], + ['name' => 'noHPOrtu', 'label' => 'No HP Orang Tua', 'value' => $row->noHPOrtu ?? ''], + ]; + } + + private function dosenFields(?object $row = null, bool $editing = false): array + { + return [ + ['name' => 'nip', 'label' => 'NIP', 'value' => $row->nip ?? '', 'required' => true], + ['name' => 'nmLengkap', 'label' => 'Nama Lengkap', 'value' => $row->nmLengkap ?? '', 'required' => true], + ['name' => 'email', 'label' => 'Email', 'type' => 'email', 'value' => $row->email ?? ''], + ['name' => 'nohp', 'label' => 'No HP', 'value' => $row->nohp ?? ''], + ['name' => 'jenis', 'label' => 'Jenis/Jabatan Dosen', 'value' => $row->jenis ?? 'D'], + ['name' => 'password', 'label' => 'Password', 'type' => 'password', 'required' => ! $editing, 'help' => $editing ? 'Kosongkan jika tidak mengganti password.' : 'Password awal dosen.'], + ['name' => 'status', 'label' => 'Status', 'type' => 'select', 'value' => $row->status ?? 'A', 'options' => ['A' => 'Aktif', 'P' => 'Pending', 'N' => 'Nonaktif']], + ]; + } + + private function kkFields(?object $row = null): array + { + $dosen = DB::table('tbdosen')->select(['iddosen', 'nmLengkap'])->orderBy('nmLengkap')->get(); + $options = ['' => '- Pilih Dosen -']; + foreach ($dosen as $item) { + $options[$item->iddosen] = $item->nmLengkap; + } + + return [ + ['name' => 'namaKK', 'label' => 'Nama Kelompok Keahlian', 'value' => $row->namaKK ?? '', 'required' => true], + ['name' => 'warnaLabel', 'label' => 'Warna Label', 'value' => $row->warnaLabel ?? '', 'help' => 'Opsional, mengikuti data legacy.'], + ['name' => 'ketuaKK', 'label' => 'Ketua KK', 'type' => 'select', 'value' => $row->ketuaKK ?? '', 'options' => $options], + ['name' => 'sekretarisKK', 'label' => 'Sekretaris KK', 'type' => 'select', 'value' => $row->sekretarisKK ?? '', 'options' => $options], + ]; + } + + private function praoutlineBase(array $user) + { + return DB::table('tbpraoutline as tp') + ->leftJoin('tbmhs as tm', 'tp.nim', '=', 'tm.nim') + ->select(['tp.id', 'tp.nim', 'tm.nmLengkap as mahasiswa', 'tp.judul', 'tp.tgl_upload', 'tp.wkt_upload', 'tp.status_usulan']) + ->where('tp.idProdi', $user['prodi']); + } + + private function user(Request $request): array + { + $auth = $request->session()->get('legacy_auth'); + abort_unless(($auth['role'] ?? null) === 'admin', 403); + + return $auth['user']; + } + + private function abortIfSuper(array $user): void + { + abort_if(($user['lvl'] ?? null) === 'S', 403); + } + + private function abortUnlessSuper(array $user): void + { + abort_unless(($user['lvl'] ?? null) === 'S', 403); + } + + private function statusLabel(?string $status): string + { + return match ($status) { + '0' => 'Dalam Review', + '1' => 'Disetujui', + '2' => 'Ditolak', + default => $status ?? '-', + }; + } + + private function formatDateTime(?string $value): string + { + if (! $value || trim($value) === '') { + return '-'; + } + + return Carbon::parse($value)->locale('id')->translatedFormat('j F Y, H:i'); + } +} diff --git a/rebuild/app/Http/Controllers/Auth/LegacyAuthController.php b/rebuild/app/Http/Controllers/Auth/LegacyAuthController.php new file mode 100644 index 0000000..387b388 --- /dev/null +++ b/rebuild/app/Http/Controllers/Auth/LegacyAuthController.php @@ -0,0 +1,272 @@ + 'Masuk ke SPOTA Rebuild', + 'intro' => [ + 'eyebrow' => 'Portal Masuk SPOTA', + 'heading' => 'Satu halaman masuk untuk memahami SPOTA sebelum memilih akun.', + 'description' => 'SPOTA adalah Sistem Pendukung Outline Tugas Akhir yang dipakai di Informatika UNTAN untuk mengelola alur usulan judul, review dosen, penawaran judul, bimbingan, pengumuman, dan monitoring progres mahasiswa.', + 'details' => [ + 'Mahasiswa menggunakan SPOTA untuk mengusulkan judul, melihat tanggapan dosen, memantau status, dan mengikuti jadwal akademik terkait tugas akhir.', + 'Dosen menggunakan SPOTA untuk meninjau usulan, memantau mahasiswa bimbingan, mengelola penawaran judul, dan melihat peringatan progres studi.', + 'Admin menggunakan SPOTA untuk mengelola data dasar, praoutline, pengumuman, jadwal seminar/sidang, dan pengaturan program studi.', + 'Halaman masuk rebuild ini tetap terhubung ke data aktif sistem lama agar transisi ke tampilan baru tidak memutus alur kerja yang sudah berjalan.', + ], + ], + 'roles' => [ + ['slug' => 'mahasiswa', 'name' => 'Masuk sebagai Mahasiswa', 'summary' => 'Untuk akses usulan judul, status review, dan jadwal terkait tugas akhir.'], + ['slug' => 'dosen', 'name' => 'Masuk sebagai Dosen', 'summary' => 'Untuk akses review usulan, penawaran judul, bimbingan, dan early warning.'], + ['slug' => 'admin', 'name' => 'Masuk sebagai Admin', 'summary' => 'Untuk akses manajemen data, praoutline, pengumuman, jadwal, dan pengaturan prodi.'], + ], + ]); + } + + public function showLegacyLogin(string $role): View + { + abort_unless(in_array($role, ['mahasiswa', 'dosen', 'admin'], true), 404); + + $roleMeta = [ + 'mahasiswa' => [ + 'title' => 'Login Mahasiswa | SPOTA Rebuild', + 'heading' => 'Masuk sebagai Mahasiswa', + 'eyebrow' => 'Akses Mahasiswa', + 'description' => 'Gunakan akun SPOTA mahasiswa yang sudah ada. Dashboard setelah login akan memakai data real dari basis data legacy.', + 'input' => 'NIM', + 'placeholder' => 'Masukkan NIM', + 'status' => 'Login aktif', + 'status_note' => 'Autentikasi mahasiswa sudah terhubung ke data SPOTA lama.', + 'summary_title' => 'SPOTA untuk Mahasiswa', + 'summary_points' => [ + 'Mengelola usulan judul tugas akhir dan membaca tanggapan dosen pembimbing atau reviewer.', + 'Memantau status proses outline, hasil review, dan informasi penting yang berhubungan dengan tugas akhir.', + 'Menggunakan akun mahasiswa yang sama dengan sistem SPOTA sebelumnya agar tidak perlu registrasi ulang.', + ], + 'help_note' => 'Masukkan NIM dan password akun SPOTA mahasiswa yang sudah aktif.', + ], + 'dosen' => [ + 'title' => 'Login Dosen | SPOTA Rebuild', + 'heading' => 'Masuk sebagai Dosen', + 'eyebrow' => 'Akses Dosen', + 'description' => 'Gunakan akun SPOTA dosen yang sudah aktif. Dashboard setelah login akan menampilkan ringkasan review, mahasiswa binaan, dan jadwal terdekat.', + 'input' => 'NIP', + 'placeholder' => 'Masukkan NIP', + 'status' => 'Login aktif', + 'status_note' => 'Autentikasi dosen sudah terhubung ke data SPOTA lama.', + 'summary_title' => 'SPOTA untuk Dosen', + 'summary_points' => [ + 'Meninjau usulan judul, membaca diskusi review, dan memantau antrean bimbingan mahasiswa.', + 'Mengelola penawaran judul, melihat pengumuman, dan memantau early warning progres studi.', + 'Tetap memakai akun dosen pada sistem SPOTA yang sudah aktif sebelumnya.', + ], + 'help_note' => 'Masukkan NIP dan password akun dosen yang aktif pada SPOTA.', + ], + 'admin' => [ + 'title' => 'Login Admin | SPOTA Rebuild', + 'heading' => 'Masuk sebagai Admin', + 'eyebrow' => 'Akses Admin', + 'description' => 'Gunakan akun admin SPOTA aktif. Dashboard admin rebuild menjaga link dan fungsi tetap mengikuti halaman administrator lama.', + 'input' => 'Username', + 'placeholder' => 'Masukkan username', + 'status' => 'Login aktif', + 'status_note' => 'Autentikasi admin sudah terhubung ke tabel tbadmin legacy.', + 'summary_title' => 'SPOTA untuk Admin', + 'summary_points' => [ + 'Mengelola data mahasiswa, dosen, kelompok keahlian, dan data akademik sesuai hak akses admin.', + 'Mengelola praoutline, pengumuman, jadwal seminar/sidang, pengguna admin, dan pengaturan prodi.', + 'Fungsi detail tetap diarahkan ke modul legacy agar tidak memutus proses yang sudah berjalan.', + ], + 'help_note' => 'Masukkan username dan password admin SPOTA yang aktif.', + ], + ]; + + return view('auth.legacy-login', [ + 'role' => $role, + 'meta' => $roleMeta[$role], + ]); + } + + public function authenticate(Request $request, string $role): RedirectResponse + { + abort_unless(in_array($role, ['mahasiswa', 'dosen', 'admin'], true), 404); + + $credentials = $request->validate([ + 'identifier' => ['required', 'string'], + 'password' => ['required', 'string'], + ]); + + if ($role === 'mahasiswa') { + $mahasiswa = DB::table('tbmhs as tm') + ->leftJoin('tbprodi as tp', 'tm.idProdi', '=', 'tp.idProdi') + ->select([ + 'tm.nim', + 'tm.idmhs', + 'tm.password', + 'tm.nmLengkap', + 'tm.idProdi', + 'tp.nmProdi', + 'tm.status', + ]) + ->where('tm.nim', $credentials['identifier']) + ->whereIn('tm.status', ['A', 'P']) + ->first(); + + if (! $mahasiswa) { + return back()->withInput()->withErrors([ + 'identifier' => 'Username Anda tidak terdaftar.', + ]); + } + + if ($mahasiswa->password !== md5($credentials['password'])) { + return back()->withInput()->withErrors([ + 'password' => 'Password anda tidak sesuai atau salah.', + ]); + } + + $request->session()->regenerate(); + $request->session()->put('legacy_auth', [ + 'role' => 'mahasiswa', + 'user' => [ + 'nim' => $mahasiswa->nim, + 'prodi' => (string) $mahasiswa->idProdi, + 'nmprodi' => $mahasiswa->nmProdi, + 'nama_lengkap' => $mahasiswa->nmLengkap, + 'id' => (string) $mahasiswa->idmhs, + 'status' => $mahasiswa->status, + ], + ]); + + return redirect()->route('dashboard.mahasiswa'); + } + + if ($role === 'dosen') { + $dosen = DB::table('tbdosen as td') + ->leftJoin('tbprodi as tp', 'td.idProdi', '=', 'tp.idProdi') + ->select([ + 'td.iddosen', + 'td.nip', + 'td.password', + 'td.nmLengkap', + 'td.email', + 'td.idProdi', + 'td.kelompokKeahlian', + 'td.jenis', + 'td.status', + 'tp.nmProdi', + ]) + ->where('td.nip', $credentials['identifier']) + ->where('td.status', 'A') + ->first(); + + if (! $dosen) { + return back()->withInput()->withErrors([ + 'identifier' => 'Username Anda tidak terdaftar.', + ]); + } + + if ($credentials['identifier'] === '0000' || $credentials['identifier'] === '123456') { + return back()->withInput()->withErrors([ + 'identifier' => 'Akun dummy tidak dapat digunakan untuk login.', + ]); + } + + if ($dosen->password !== md5($credentials['password'])) { + return back()->withInput()->withErrors([ + 'password' => 'Password anda tidak sesuai atau salah.', + ]); + } + + $request->session()->regenerate(); + $request->session()->put('legacy_auth', [ + 'role' => 'dosen', + 'user' => [ + 'nip' => $dosen->nip, + 'prodi' => (string) $dosen->idProdi, + 'nmprodi' => $dosen->nmProdi, + 'nama_lengkap' => $dosen->nmLengkap, + 'id' => (string) $dosen->iddosen, + 'jenisdosen' => $dosen->jenis, + 'kelompokKeahlian' => $dosen->kelompokKeahlian, + 'email' => $dosen->email, + 'status' => $dosen->status, + ], + ]); + + return redirect()->route('dashboard.dosen'); + } + + $admin = DB::table('tbadmin as ta') + ->leftJoin('tbprodi as tp', 'ta.idProdi', '=', 'tp.idProdi') + ->select([ + 'ta.idAdmin', + 'ta.username', + 'ta.password', + 'ta.nmLengkap', + 'ta.jabatan', + 'ta.email', + 'ta.idProdi', + 'ta.jenisAdmin', + 'ta.aktif', + 'tp.nmProdi', + ]) + ->where('ta.username', $credentials['identifier']) + ->where('ta.aktif', 'Y') + ->first(); + + if (! $admin) { + return back()->withInput()->withErrors([ + 'identifier' => 'Username Anda tidak terdaftar.', + ]); + } + + if (strtolower($credentials['identifier']) === 'dummyadmin') { + return back()->withInput()->withErrors([ + 'identifier' => 'Akun dummy tidak dapat digunakan untuk login.', + ]); + } + + if ($admin->password !== md5($credentials['password'])) { + return back()->withInput()->withErrors([ + 'password' => 'Password anda tidak sesuai atau salah.', + ]); + } + + $request->session()->regenerate(); + $request->session()->put('legacy_auth', [ + 'role' => 'admin', + 'user' => [ + 'username' => $admin->username, + 'prodi' => (string) $admin->idProdi, + 'nmprodi' => $admin->nmProdi ?: 'Semua Program Studi', + 'lvl' => $admin->jenisAdmin, + 'nama_lengkap' => $admin->nmLengkap, + 'id' => (string) $admin->idAdmin, + 'jabatan' => $admin->jabatan, + 'email' => $admin->email, + 'status' => $admin->aktif, + ], + ]); + + return redirect()->route('dashboard.admin'); + } + + public function logout(Request $request): RedirectResponse + { + $request->session()->forget('legacy_auth'); + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return redirect()->route('role-login'); + } +} diff --git a/rebuild/app/Http/Controllers/Controller.php b/rebuild/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..8677cd5 --- /dev/null +++ b/rebuild/app/Http/Controllers/Controller.php @@ -0,0 +1,8 @@ +session()->get('legacy_auth'); + + abort_unless(($auth['role'] ?? null) === 'admin', 403); + + $user = $auth['user']; + $isSuper = ($user['lvl'] ?? null) === 'S'; + + $stats = $isSuper ? [ + ['label' => 'Fakultas', 'value' => (string) DB::table('tbfakultas')->count(), 'note' => 'Total data fakultas pada basis data SPOTA.'], + ['label' => 'Jurusan', 'value' => (string) DB::table('tbjurusan')->count(), 'note' => 'Total data jurusan lintas fakultas.'], + ['label' => 'Program Studi', 'value' => (string) DB::table('tbprodi')->count(), 'note' => 'Total program studi yang tercatat.'], + ['label' => 'Admin Aktif', 'value' => (string) DB::table('tbadmin')->where('aktif', 'Y')->count(), 'note' => 'Akun administrator aktif.'], + ] : [ + ['label' => 'Mahasiswa', 'value' => (string) DB::table('tbmhs')->where('idProdi', $user['prodi'])->count(), 'note' => 'Data mahasiswa pada program studi admin.'], + ['label' => 'Dosen', 'value' => (string) DB::table('tbdosen')->where('idProdi', $user['prodi'])->count(), 'note' => 'Data dosen pada program studi admin.'], + ['label' => 'Draft Praoutline', 'value' => (string) DB::table('tbpraoutline')->where('idProdi', $user['prodi'])->count(), 'note' => 'Total draft praoutline program studi.'], + ['label' => 'Jadwal Publish', 'value' => (string) DB::table('tbjadwal')->where('idProdi', $user['prodi'])->where('publish', 'Y')->count(), 'note' => 'Jadwal seminar/sidang yang dipublikasi.'], + ]; + + $schedules = DB::table('tbjadwal as tj') + ->leftJoin('tbmhs as tm', 'tj.idMhs', '=', 'tm.idmhs') + ->select(['tj.jenis', 'tj.start', 'tj.ruangan', 'tm.nmLengkap as mahasiswa']) + ->when(! $isSuper, fn ($query) => $query->where('tj.idProdi', $user['prodi'])) + ->whereNotNull('tj.start') + ->orderByDesc('tj.start') + ->limit(6) + ->get() + ->map(fn ($item) => [ + 'jenis' => $this->scheduleLabel($item->jenis), + 'tanggal' => $this->formatDateTime($item->start), + 'ruangan' => $item->ruangan ?: '-', + 'mahasiswa' => $item->mahasiswa ?: '-', + ]) + ->all(); + + return view('dashboard.admin', [ + 'title' => 'Dashboard Admin | SPOTA Rebuild', + 'user' => $user, + 'sidebar' => AdminNavigation::build($user), + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'stats' => $stats, + 'schedules' => $schedules, + ]); + } + + private function scheduleLabel(?string $jenis): string + { + return match ($jenis) { + 'Sidang' => 'Sidang', + 'Outline' => 'Outline', + 'SidHas' => 'Seminar Hasil', + default => $jenis ?? 'Jadwal', + }; + } + + private function formatDateTime(?string $value): string + { + if (! $value) { + return '-'; + } + + return Carbon::parse($value)->locale('id')->translatedFormat('j F Y, H:i'); + } +} diff --git a/rebuild/app/Http/Controllers/Dashboard/DosenDashboardController.php b/rebuild/app/Http/Controllers/Dashboard/DosenDashboardController.php new file mode 100644 index 0000000..73815bc --- /dev/null +++ b/rebuild/app/Http/Controllers/Dashboard/DosenDashboardController.php @@ -0,0 +1,274 @@ +session()->get('legacy_auth'); + + abort_unless(($auth['role'] ?? null) === 'dosen', 403); + + $user = $auth['user']; + + $cachePrefix = 'rebuild:dosen:dashboard:'.$user['prodi'].':'.$user['id']; + $today = now(); + $startOfMonth = $today->copy()->startOfMonth(); + $endOfMonth = $today->copy()->endOfMonth(); + + $unreadAnnouncements = Cache::remember($cachePrefix.':unread-announcements', 60, function () use ($user) { + return DB::table('tbpengumuman') + ->where('idProdi', $user['prodi']) + ->whereIn('tujuan', ['A', 'D']) + ->whereNotIn('id', function ($query) use ($user) { + $query->select('idkonten') + ->from('tmp_notif') + ->where('iduser', $user['id']) + ->where('typeuser', 'D') + ->where('jenis', 'P'); + }) + ->count(); + }); + + $unreadProposals = Cache::remember($cachePrefix.':unread-proposals', 60, function () use ($user) { + return DB::table('tbpraoutline') + ->where('status_usulan', '0') + ->whereNotIn('id', function ($query) use ($user) { + $query->select('idkonten') + ->from('tmp_notif') + ->where('iduser', $user['id']) + ->where('typeuser', 'D') + ->where('jenis', 'J'); + }) + ->count(); + }); + + $scheduleRows = Cache::remember($cachePrefix.':calendar:'.$startOfMonth->format('Y-m'), 60, function () use ($user, $startOfMonth, $endOfMonth) { + return DB::table('tbjadwal as tj') + ->leftJoin('tbmhs as tm', 'tm.idmhs', '=', 'tj.idmhs') + ->select([ + 'tj.id', + 'tj.jenis', + 'tj.start', + 'tm.nmLengkap', + ]) + ->where('tj.publish', 'Y') + ->where('tj.idProdi', $user['prodi']) + ->whereBetween('tj.start', [$startOfMonth->toDateTimeString(), $endOfMonth->copy()->endOfDay()->toDateTimeString()]) + ->orderBy('tj.start') + ->get(); + }); + + $upcomingSchedules = Cache::remember($cachePrefix.':upcoming', 60, function () use ($user, $today) { + return DB::table('tbjadwal as tj') + ->leftJoin('tbmhs as tm', 'tm.idmhs', '=', 'tj.idmhs') + ->select([ + 'tj.id', + 'tj.jenis', + 'tj.start', + 'tj.ruangan', + 'tj.judul', + 'tm.nim', + 'tm.nmLengkap', + ]) + ->where('tj.publish', 'Y') + ->where('tj.idProdi', $user['prodi']) + ->whereNotNull('tj.start') + ->where('tj.start', '>=', $today->copy()->startOfDay()->toDateTimeString()) + ->orderBy('tj.start') + ->limit(5) + ->get() + ->map(fn ($row) => [ + 'id' => $row->id, + 'jenis' => $this->scheduleLabel($row->jenis), + 'jenisClass' => $this->scheduleBadgeClass($row->jenis), + 'nama' => $row->nmLengkap ?: 'Mahasiswa tidak ditemukan', + 'nim' => $row->nim ?: '-', + 'judul' => $row->judul ?: 'Judul belum tersedia', + 'ruangan' => $row->ruangan ?: '-', + 'tanggal' => $this->formatIndonesianDateTime($row->start), + ]) + ->all(); + }); + + $calendar = $this->buildCalendar($scheduleRows); + + $dashboard = [ + 'dateLabel' => $this->formatIndonesianDateTime(now()->toDateTimeString()), + 'pageTitle' => 'Dashboard', + 'sidebar' => DosenNavigation::build('dashboard.dosen'), + 'menus' => [ + ['title' => 'Dashboard', 'href' => route('dashboard.dosen'), 'icon' => 'home', 'active' => true], + ], + 'legacyMenus' => [ + 'Utama' => [ + ['title' => 'Penawaran Judul', 'href' => $this->legacyUrl('dosen/dashboard.php?page=penawaran&menu=list-judul-saya'), 'icon' => 'briefcase'], + ], + 'Tugas Akhir 1' => [ + ['title' => 'Daftar Usulan', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=new'), 'icon' => 'folder'], + ['title' => 'Review Saya', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=myreview'), 'icon' => 'chat'], + ['title' => 'Pencarian Usulan', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=cari'), 'icon' => 'search'], + ['title' => 'Daftar Bimbingan Saya', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=keputusan'), 'icon' => 'users'], + ['title' => 'Statistik Usulan', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=statistik'), 'icon' => 'chart'], + ['title' => 'Pemberitahuan', 'href' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=pemberitahuan'), 'icon' => 'bell'], + ], + 'Lainnya' => [ + ['title' => 'Pengumuman', 'href' => $this->legacyUrl('dosen/dashboard.php?page=pengumuman'), 'icon' => 'megaphone'], + ['title' => 'Akun Pengguna', 'href' => $this->legacyUrl('dosen/dashboard.php?page=user&menu=my-profile'), 'icon' => 'user'], + ['title' => 'Dokumen Sidang', 'href' => 'https://edoxid.untan.ac.id/', 'icon' => 'document'], + ['title' => 'Early Warning', 'href' => $this->legacyUrl('dosen/dashboard.php?page=early-warning'), 'icon' => 'warning'], + ['title' => 'Konsultasi Skripsi', 'href' => 'https://spota.untan.ac.id/konsultasi/', 'icon' => 'chat'], + ['title' => 'Statistik Seminar', 'href' => 'https://spota.untan.ac.id/cek_banyak_sidang.php', 'icon' => 'chart'], + ['title' => 'Konsultasi KP', 'href' => 'https://informatika.untan.ac.id/konsultasi/', 'icon' => 'chat'], + ['title' => 'Pra LIRS (Dosen PA)', 'href' => $this->legacyUrl('dosen/dashboard.php?page=pra-lirs'), 'icon' => 'clipboard'], + ['title' => 'Evaluasi Mahasiswa', 'href' => 'https://spota.untan.ac.id/steven/rekapMahasiswaEvaluasi.php?angkatan='.(date('Y') - 5).'&show=belumlulus', 'icon' => 'chart'], + ], + ], + 'welcomeTitle' => 'Yth. Bapak/Ibu '.$user['nama_lengkap'], + 'welcomeText' => 'Selamat datang di Sistem Pendukung Outline Tugas Akhir (SPOTA) Universitas Tanjungpura', + 'androidLink' => $user['prodi'] === '2' ? $this->legacyUrl('spotaif.apk') : null, + 'announcementNotice' => [ + 'count' => $unreadAnnouncements, + 'title' => 'Pengumuman Terbaru', + 'message' => $unreadAnnouncements > 0 + ? 'Terdapat '.$unreadAnnouncements.' Pengumuman Terbaru Yang Belum Dibaca' + : 'Tidak Ada Pengumuman Terbaru', + 'primaryLabel' => 'Lihat Semua Pengumuman', + 'primaryHref' => $this->legacyUrl('dosen/dashboard.php?page=pengumuman'), + ], + 'proposalNotice' => [ + 'count' => $unreadProposals, + 'title' => 'Usulan Terbaru', + 'message' => $unreadProposals > 0 + ? 'Terdapat '.$unreadProposals.' Usulan Terbaru.' + : 'Tidak terdapat Usulan terbaru.', + 'primaryLabel' => $unreadProposals > 0 ? 'Lihat Usulan Terbaru' : 'Lihat Semua Usulan', + 'primaryHref' => $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=new'), + 'secondaryLabel' => $unreadProposals > 0 ? null : 'Cari Usulan', + 'secondaryHref' => $unreadProposals > 0 ? null : $this->legacyUrl('dosen/dashboard.php?page=praoutline&menu=cari'), + ], + 'calendar' => $calendar, + 'upcomingSchedules' => $upcomingSchedules, + ]; + + return view('dashboard.dosen', [ + 'title' => 'Dashboard Dosen | SPOTA Rebuild', + 'dashboard' => $dashboard, + 'user' => $user, + ]); + } + + private function buildCalendar($scheduleRows): array + { + $today = now(); + $startOfMonth = $today->copy()->startOfMonth(); + $endOfMonth = $today->copy()->endOfMonth(); + $startOfGrid = $startOfMonth->copy()->startOfWeek(Carbon::MONDAY); + $endOfGrid = $endOfMonth->copy()->endOfWeek(Carbon::SUNDAY); + + $eventsByDate = []; + + foreach ($scheduleRows as $row) { + if (! $row->start || strtotime($row->start) === false) { + continue; + } + + $dateKey = Carbon::parse($row->start)->toDateString(); + $eventsByDate[$dateKey][] = [ + 'id' => $row->id, + 'title' => ucwords(strtolower((string) ($row->nmLengkap ?: 'Tanpa Nama'))), + 'jenis' => $this->scheduleLabel($row->jenis), + 'className' => $this->scheduleEventClass($row->jenis), + ]; + } + + $weeks = []; + $cursor = $startOfGrid->copy(); + + while ($cursor->lessThanOrEqualTo($endOfGrid)) { + $week = []; + + for ($day = 0; $day < 7; $day++) { + $dateKey = $cursor->toDateString(); + + $week[] = [ + 'date' => $dateKey, + 'day' => $cursor->day, + 'isCurrentMonth' => $cursor->month === $today->month, + 'isToday' => $cursor->isToday(), + 'events' => $eventsByDate[$dateKey] ?? [], + ]; + + $cursor->addDay(); + } + + $weeks[] = $week; + } + + return [ + 'monthLabel' => $this->formatMonthYear($today), + 'weekdays' => ['Sen', 'Sel', 'Rab', 'Kam', 'Jum', 'Sab', 'Min'], + 'weeks' => $weeks, + ]; + } + + private function scheduleBadgeClass(?string $jenis): string + { + return match ($jenis) { + 'Sidang' => 'bg-orange-100 text-orange-700', + 'Outline' => 'bg-emerald-100 text-emerald-700', + 'SidHas' => 'bg-amber-100 text-amber-700', + default => 'bg-slate-200 text-slate-700', + }; + } + + private function scheduleLabel(?string $jenis): string + { + return match ($jenis) { + 'Sidang' => 'Sidang', + 'Outline' => 'Outline', + 'SidHas' => 'Seminar Hasil', + default => $jenis ?? 'Jadwal', + }; + } + + private function scheduleEventClass(?string $jenis): string + { + return match ($jenis) { + 'Sidang' => 'bg-orange-100 text-orange-700 border-orange-200', + 'Outline' => 'bg-emerald-100 text-emerald-700 border-emerald-200', + 'SidHas' => 'bg-amber-100 text-amber-700 border-amber-200', + default => 'bg-slate-100 text-slate-700 border-slate-200', + }; + } + + private function formatIndonesianDateTime(?string $value): string + { + if (! $value) { + return '-'; + } + + return Carbon::parse($value)->locale('id')->translatedFormat('j F Y, H:i'); + } + + private function formatMonthYear(Carbon $date): string + { + return $date->copy()->locale('id')->translatedFormat('F Y'); + } + + private function legacyUrl(string $path): string + { + $baseUrl = rtrim((string) env('LEGACY_BASE_URL', 'http://127.0.0.1:8080'), '/'); + + return $baseUrl.'/'.ltrim($path, '/'); + } +} diff --git a/rebuild/app/Http/Controllers/Dashboard/MahasiswaDashboardController.php b/rebuild/app/Http/Controllers/Dashboard/MahasiswaDashboardController.php new file mode 100644 index 0000000..ce13c69 --- /dev/null +++ b/rebuild/app/Http/Controllers/Dashboard/MahasiswaDashboardController.php @@ -0,0 +1,181 @@ +session()->get('legacy_auth'); + + abort_unless(($auth['role'] ?? null) === 'mahasiswa', 403); + + $user = $auth['user']; + + $latestPraoutline = DB::table('tbpraoutline as tp') + ->leftJoin('tbrekaphasil as trh', 'tp.id', '=', 'trh.idpraoutline') + ->select(['tp.id', 'tp.judul', 'tp.status_usulan', 'trh.ket']) + ->where('tp.nim', $user['nim']) + ->where('tp.idProdi', $user['prodi']) + ->orderByDesc('tp.id') + ->first(); + + $unreadAnnouncements = DB::table('tbpengumuman') + ->where('idProdi', $user['prodi']) + ->whereIn('tujuan', ['A', 'M']) + ->whereNotIn('id', function ($query) use ($user) { + $query->select('idkonten') + ->from('tmp_notif') + ->where('iduser', $user['id']) + ->where('typeuser', 'M') + ->where('jenis', 'P'); + }) + ->count(); + + $publishedSchedules = DB::table('tbjadwal') + ->where('idProdi', $user['prodi']) + ->where('publish', 'Y') + ->count(); + + $nextSchedule = DB::table('tbjadwal') + ->select(['jenis', 'start', 'ruangan']) + ->where('idMhs', $user['id']) + ->where('publish', 'Y') + ->whereNotNull('start') + ->orderBy('start') + ->first(); + + $activeDrafts = DB::table('tbpraoutline') + ->where('nim', $user['nim']) + ->whereIn('status_usulan', ['0', '1']) + ->count(); + + $statusAlert = match ($latestPraoutline?->status_usulan) { + '0' => [ + 'title' => 'Draft Praoutline Masih Dalam Proses Review', + 'description' => 'Silakan cek menu review untuk melihat tanggapan dosen terhadap draft yang sedang berjalan.', + 'button' => 'Lihat Review', + 'buttonHref' => route('mahasiswa.status-usulan', [], false), + 'class' => 'border-sky-200 bg-sky-50 text-sky-900', + 'buttonClass' => 'bg-sky-600 text-white hover:opacity-90', + ], + '1' => [ + 'title' => 'Draft Praoutline Anda Telah Disetujui', + 'description' => 'Judul terakhir sudah memperoleh persetujuan. Silakan pantau tahapan berikutnya pada modul yang terkait.', + 'button' => 'Lihat Putusan', + 'buttonHref' => route('mahasiswa.status-usulan', [], false), + 'class' => 'border-emerald-200 bg-emerald-50 text-emerald-900', + 'buttonClass' => 'bg-emerald-600 text-white hover:opacity-90', + ], + '2' => [ + 'title' => 'Judul yang Anda Ajukan Tidak Disetujui', + 'description' => $latestPraoutline?->ket ?: 'Silakan periksa catatan keputusan dan siapkan upload judul baru.', + 'button' => 'Lihat Catatan Putusan', + 'buttonHref' => route('mahasiswa.status-usulan', [], false), + 'class' => 'border-rose-200 bg-rose-50 text-rose-900', + 'buttonClass' => 'bg-rose-600 text-white hover:opacity-90', + ], + default => [ + 'title' => 'Belum Ada Draft Praoutline Aktif', + 'description' => 'Mahasiswa ini belum memiliki usulan judul aktif yang tercatat pada data SPOTA saat ini.', + 'button' => 'Ajukan Outline Baru', + 'buttonHref' => route('mahasiswa.praoutline.upload', [], false), + 'class' => 'border-slate-200 bg-slate-50 text-slate-900', + 'buttonClass' => 'bg-[#15171A] text-white hover:opacity-90', + ], + }; + + $announcementAlert = [ + 'title' => 'Pengumuman Terbaru', + 'description' => $unreadAnnouncements > 0 + ? 'Terdapat '.$unreadAnnouncements.' pengumuman program studi yang belum dibaca.' + : 'Tidak terdapat pengumuman baru yang perlu dibaca saat ini.', + 'button' => 'Lihat Semua Pengumuman', + 'buttonHref' => route('mahasiswa.pengumuman.index', [], false), + 'class' => 'border-amber-200 bg-amber-50 text-amber-900', + 'buttonClass' => 'bg-amber-400 text-[#15171A] hover:bg-amber-300', + ]; + + $dashboard = [ + 'eyebrow' => 'Dashboard Mahasiswa', + 'title' => 'Halo, '.$user['nama_lengkap'].'.', + 'description' => 'Selamat datang di Sistem Pendukung Outline Tugas Akhir (SPOTA) Universitas Tanjungpura.', + 'menus' => [ + ['title' => 'Dashboard', 'href' => route('dashboard.mahasiswa'), 'icon' => 'home', 'active' => true], + ['title' => 'Informasi', 'icon' => 'folder', 'children' => [ + ['title' => 'Status Usulan', 'href' => route('mahasiswa.status-usulan', [], false), 'icon' => 'chart'], + ['title' => 'Ajukan Outline Baru', 'href' => route('mahasiswa.praoutline.upload', [], false), 'icon' => 'clipboard'], + ['title' => 'Penawaran Judul', 'href' => route('mahasiswa.penawaran.index', [], false), 'icon' => 'briefcase'], + ['title' => 'Pengumuman', 'href' => route('mahasiswa.pengumuman.index', [], false), 'icon' => 'bell'], + ['title' => 'Jadwal Terdekat', 'href' => '#jadwal', 'icon' => 'clock'], + ]], + ], + 'stats' => [ + ['label' => 'Status Usulan Terakhir', 'value' => $this->statusLabel($latestPraoutline?->status_usulan), 'delta' => $latestPraoutline ? 'Draft #'.$latestPraoutline->id : 'Belum ada draft', 'deltaClass' => $this->statusDeltaClass($latestPraoutline?->status_usulan), 'note' => $latestPraoutline?->judul ? $latestPraoutline->judul : 'Belum ada judul yang tercatat di sistem.'], + ['label' => 'Pengumuman Belum Dibaca', 'value' => (string) $unreadAnnouncements, 'delta' => $unreadAnnouncements > 0 ? 'Perlu dibaca' : 'Sudah aman', 'deltaClass' => $unreadAnnouncements > 0 ? 'bg-amber-100 text-amber-800' : 'bg-emerald-100 text-emerald-800', 'note' => 'Jumlah pengumuman program studi yang belum dibaca.'], + ['label' => 'Jadwal Terdekat', 'value' => $nextSchedule ? $this->scheduleLabel($nextSchedule->jenis) : '-', 'delta' => $nextSchedule ? 'Terjadwal' : 'Belum ada jadwal', 'deltaClass' => $nextSchedule ? 'bg-sky-100 text-sky-800' : 'bg-slate-200 text-slate-700', 'note' => $nextSchedule ? $this->formatDateTime($nextSchedule->start).' · '.$nextSchedule->ruangan : 'Belum ada jadwal seminar yang dipublikasikan untuk mahasiswa ini.'], + ['label' => 'Draft Aktif', 'value' => (string) $activeDrafts, 'delta' => $activeDrafts > 0 ? 'Masih berjalan' : 'Tidak ada', 'deltaClass' => $activeDrafts > 0 ? 'bg-violet-100 text-violet-800' : 'bg-slate-200 text-slate-700', 'note' => 'Jumlah draft yang masih aktif pada data SPOTA.'], + ], + 'latestTitle' => $latestPraoutline?->judul, + 'statusAlert' => $statusAlert, + 'announcementAlert' => $announcementAlert, + 'nextSchedule' => $nextSchedule ? [ + 'jenis' => $this->scheduleLabel($nextSchedule->jenis), + 'tanggal' => $this->formatDateTime($nextSchedule->start), + 'ruangan' => $nextSchedule->ruangan ?: '-', + ] : null, + 'publishedSchedules' => $publishedSchedules, + ]; + + return view('dashboard.mahasiswa', [ + 'title' => 'Dashboard Mahasiswa | SPOTA Rebuild', + 'dashboard' => $dashboard, + 'user' => $user, + ]); + } + + private function statusLabel(?string $status): string + { + return match ($status) { + '0' => 'Dalam Review', + '1' => 'Disetujui', + '2' => 'Ditolak', + default => 'Belum Ada Draft', + }; + } + + private function statusDeltaClass(?string $status): string + { + return match ($status) { + '0' => 'bg-sky-100 text-sky-800', + '1' => 'bg-emerald-100 text-emerald-800', + '2' => 'bg-rose-100 text-rose-800', + default => 'bg-slate-200 text-slate-700', + }; + } + + private function scheduleLabel(?string $jenis): string + { + return match ($jenis) { + 'Sidang' => 'Sidang', + 'Outline' => 'Outline', + 'SidHas' => 'Seminar Hasil', + default => $jenis ?? 'Jadwal', + }; + } + + private function formatDateTime(?string $value): string + { + if (! $value) { + return '-'; + } + + return Carbon::parse($value)->locale('id')->translatedFormat('j F Y, H:i'); + } +} diff --git a/rebuild/app/Http/Controllers/DosenPageController.php b/rebuild/app/Http/Controllers/DosenPageController.php new file mode 100644 index 0000000..5b7247d --- /dev/null +++ b/rebuild/app/Http/Controllers/DosenPageController.php @@ -0,0 +1,962 @@ +dosenUser($request); + + $source = (string) $request->query('sumber', '0'); + $statusFilter = (string) $request->query('status', 'Semua'); + $kkFilter = (string) $request->query('kk', 'all'); + + $kkOptions = DB::table('tb_kelompok_keahlian') + ->where('idKK', '!=', '8') + ->orderBy('namaKK') + ->get(['idKK', 'namaKK']) + ->map(fn ($item) => [ + 'value' => (string) $item->idKK, + 'label' => $item->namaKK, + ]) + ->all(); + + $penawaranQuery = DB::table('tb_penawaran_judul as tpj') + ->leftJoin('tb_ambil_judul as taj', function ($join) { + $join->on('taj.idPenawaranAmbil', '=', 'tpj.idPenawaran') + ->whereRaw('taj.waktuPengambilan = (SELECT MAX(t2.waktuPengambilan) FROM tb_ambil_judul t2 WHERE t2.idPenawaranAmbil = tpj.idPenawaran)'); + }) + ->leftJoin('tbmhs as tm', 'tm.idmhs', '=', 'taj.idMhs') + ->leftJoin('tb_kelompok_keahlian as tkk', 'tkk.idKK', '=', 'tpj.kk') + ->leftJoin('tbdosen as td', 'td.iddosen', '=', 'tpj.idDosen') + ->select([ + 'tpj.idPenawaran', + 'tpj.idDosen', + 'tpj.judul', + 'tpj.deskripsi', + 'tpj.waktuInput', + 'taj.statusPengambilan', + 'tm.nim', + 'tm.nmLengkap', + 'tkk.namaKK', + 'td.nmLengkap as namaDosen', + ]) + ->where('td.idProdi', $user['prodi']); + + if ($source === '0') { + $penawaranQuery->where('tpj.idDosen', $user['id']); + } + + if ($kkFilter !== 'all') { + $penawaranQuery->where('tpj.kk', $kkFilter); + } + + if ($statusFilter === 'Belum Diambil') { + $penawaranQuery->where(function ($query) { + $query->whereNull('taj.statusPengambilan') + ->orWhere('taj.statusPengambilan', '2'); + }); + } elseif ($statusFilter === 'Belum Diproses') { + $penawaranQuery->where('taj.statusPengambilan', '0'); + } elseif ($statusFilter === 'Diterima') { + $penawaranQuery->where('taj.statusPengambilan', '1'); + } + + $penawaran = $penawaranQuery + ->orderByDesc('tpj.waktuInput') + ->limit(100) + ->get() + ->map(function ($item) use ($user) { + $takenBy = $item->nmLengkap ? $item->nmLengkap.' ('.$item->nim.')' : '-'; + + if ((string) $item->statusPengambilan === '2') { + $takenBy = '-'; + } + + return [ + 'id' => $item->idPenawaran, + 'judul' => $item->judul, + 'deskripsi' => $item->deskripsi ?: '-', + 'waktu' => $this->formatDateTime($item->waktuInput), + 'kk' => $item->namaKK ?: '-', + 'ditawarkanOleh' => $item->namaDosen ?: '-', + 'mahasiswa' => $takenBy, + 'status' => $this->statusPenawaran($item->statusPengambilan), + 'editHref' => route('dosen.penawaran.edit', $item->idPenawaran), + 'isMine' => (string) $item->idDosen === (string) $user['id'], + 'destroyHref' => route('dosen.penawaran.destroy', $item->idPenawaran), + 'approveHref' => route('dosen.penawaran.approve', $item->idPenawaran), + 'rejectHref' => route('dosen.penawaran.reject', $item->idPenawaran), + 'canApprove' => (string) $item->statusPengambilan === '0', + ]; + }) + ->all(); + + return $this->renderPage('dosen.pages.penawaran', [ + 'title' => 'Penawaran Judul | SPOTA Rebuild', + 'pageTitle' => 'Penawaran Judul', + 'pageDescription' => 'Daftar penawaran judul milik dosen yang sudah masuk pada sistem lama.', + 'routeName' => 'dosen.penawaran.index', + 'user' => $user, + 'penawaran' => $penawaran, + 'source' => $source, + 'statusFilter' => $statusFilter, + 'kkFilter' => $kkFilter, + 'kkOptions' => $kkOptions, + 'pageActions' => [ + ['label' => 'Tambah Data', 'href' => route('dosen.penawaran.create'), 'variant' => 'dark'], + ], + ]); + } + + public function createPenawaran(Request $request): View + { + $user = $this->dosenUser($request); + + return $this->renderPage('dosen.pages.penawaran-form', [ + 'title' => 'Tambah Penawaran Judul | SPOTA Rebuild', + 'pageTitle' => 'Tambah Data Penawaran Judul', + 'pageDescription' => 'Tambahkan data penawaran judul baru langsung dari halaman rebuild.', + 'routeName' => 'dosen.penawaran.index', + 'user' => $user, + 'formMode' => 'create', + 'formAction' => route('dosen.penawaran.store'), + 'formMethod' => 'POST', + 'penawaranItem' => [ + 'judul' => old('judul_penawaran', ''), + 'deskripsi' => old('keterangan_penawaran', ''), + ], + ]); + } + + public function storePenawaran(Request $request): RedirectResponse + { + $user = $this->dosenUser($request); + + $data = $request->validate([ + 'judul_penawaran' => ['required', 'string', 'max:255'], + 'keterangan_penawaran' => ['nullable', 'string'], + ]); + + $kk = DB::table('tbdosen') + ->where('iddosen', $user['id']) + ->value('kelompokKeahlian'); + + DB::table('tb_penawaran_judul')->insert([ + 'idDosen' => $user['id'], + 'kk' => $kk ?: 0, + 'judul' => trim($data['judul_penawaran']), + 'deskripsi' => trim((string) ($data['keterangan_penawaran'] ?? '')), + 'waktuInput' => now(), + ]); + + return redirect()->route('dosen.penawaran.index')->with('success', 'Data Penawaran Judul Berhasil Disimpan'); + } + + public function editPenawaran(Request $request, int $id): View + { + $user = $this->dosenUser($request); + + $penawaran = DB::table('tb_penawaran_judul') + ->where('idPenawaran', $id) + ->where('idDosen', $user['id']) + ->first(); + + abort_unless($penawaran, 404); + + return $this->renderPage('dosen.pages.penawaran-form', [ + 'title' => 'Edit Penawaran Judul | SPOTA Rebuild', + 'pageTitle' => 'Edit Data Penawaran Judul', + 'pageDescription' => 'Perbarui data penawaran judul milik dosen ini.', + 'routeName' => 'dosen.penawaran.index', + 'user' => $user, + 'formMode' => 'edit', + 'formAction' => route('dosen.penawaran.update', $id), + 'formMethod' => 'PUT', + 'penawaranItem' => [ + 'judul' => old('judul_penawaran', $penawaran->judul), + 'deskripsi' => old('keterangan_penawaran', $penawaran->deskripsi), + ], + ]); + } + + public function updatePenawaran(Request $request, int $id): RedirectResponse + { + $user = $this->dosenUser($request); + + $data = $request->validate([ + 'judul_penawaran' => ['required', 'string', 'max:255'], + 'keterangan_penawaran' => ['nullable', 'string'], + ]); + + $updated = DB::table('tb_penawaran_judul') + ->where('idPenawaran', $id) + ->where('idDosen', $user['id']) + ->update([ + 'judul' => trim($data['judul_penawaran']), + 'deskripsi' => trim((string) ($data['keterangan_penawaran'] ?? '')), + ]); + + abort_unless($updated !== 0, 404); + + return redirect()->route('dosen.penawaran.index')->with('success', 'Data berhasil diubah'); + } + + public function destroyPenawaran(Request $request, int $id): RedirectResponse + { + $user = $this->dosenUser($request); + + $hasBooking = DB::table('tb_ambil_judul') + ->where('idPenawaranAmbil', $id) + ->exists(); + + if ($hasBooking) { + return redirect()->route('dosen.penawaran.index')->with('error', 'Tidak dapat menghapus judul ini, judul ini pernah di booking mahasiswa sebelumnya'); + } + + $deleted = DB::table('tb_penawaran_judul') + ->where('idPenawaran', $id) + ->where('idDosen', $user['id']) + ->delete(); + + abort_unless($deleted !== 0, 404); + + return redirect()->route('dosen.penawaran.index')->with('success', 'Data Penawaran Judul Ini Telah Dihapus.'); + } + + public function approvePenawaran(Request $request, int $id): RedirectResponse + { + $user = $this->dosenUser($request); + $booking = $this->latestPenawaranBooking($id, $user['id']); + + if (! $booking) { + return redirect()->route('dosen.penawaran.index')->with('error', 'Aksi gagal, data pengambilan judul tidak ditemukan.'); + } + + DB::table('tb_ambil_judul') + ->where('idAmbil', $booking->idAmbil) + ->update([ + 'statusPengambilan' => '1', + 'waktuVerifikasi' => now(), + ]); + + return redirect()->route('dosen.penawaran.index')->with('success', 'Berhasil menyetujui pengambilan judul.'); + } + + public function rejectPenawaran(Request $request, int $id): RedirectResponse + { + $user = $this->dosenUser($request); + $booking = $this->latestPenawaranBooking($id, $user['id']); + + if (! $booking) { + return redirect()->route('dosen.penawaran.index')->with('error', 'Aksi gagal, data pengambilan judul tidak ditemukan.'); + } + + DB::table('tb_ambil_judul') + ->where('idAmbil', $booking->idAmbil) + ->update([ + 'statusPengambilan' => '2', + 'waktuVerifikasi' => now(), + ]); + + return redirect()->route('dosen.penawaran.index')->with('success', 'Berhasil menolak pengambilan judul.'); + } + + public function daftarUsulan(Request $request): View + { + $user = $this->dosenUser($request); + + $usulan = DB::table('tbpraoutline as tp') + ->leftJoin('tbmhs as tm', 'tm.nim', '=', 'tp.nim') + ->leftJoin('tb_kelompok_keahlian as tkk', 'tkk.idKK', '=', 'tp.kelompokKeahlian') + ->select([ + 'tp.id', + 'tp.judul', + 'tp.nim', + 'tm.nmLengkap', + 'tp.thn_ajaran', + 'tp.semester', + 'tp.tgl_upload', + 'tp.wkt_upload', + 'tp.status_usulan', + 'tkk.namaKK', + ]) + ->where('tp.idProdi', $user['prodi']) + ->where('tp.status_usulan', '0') + ->orderByDesc('tp.tgl_upload') + ->orderByDesc('tp.wkt_upload') + ->limit(30) + ->get() + ->map(fn ($item) => [ + 'id' => $item->id, + 'judul' => $item->judul, + 'mahasiswa' => ($item->nmLengkap ?: 'Mahasiswa tidak ditemukan').' ('.$item->nim.')', + 'periode' => trim(($item->thn_ajaran ?: '-').' / '.($item->semester ?: '-')), + 'tanggal' => $this->formatDateTime(trim(($item->tgl_upload ?: '').' '.($item->wkt_upload ?: ''))), + 'kk' => $item->namaKK ?: '-', + 'status' => $this->statusUsulan($item->status_usulan), + 'reviewHref' => route('dosen.praoutline.review', $item->id, false), + ]) + ->all(); + + return $this->renderPage('dosen.pages.daftar-usulan', [ + 'title' => 'Daftar Usulan | SPOTA Rebuild', + 'pageTitle' => 'Daftar Usulan', + 'pageDescription' => 'Usulan judul terbaru yang masih dalam proses pada program studi dosen.', + 'routeName' => 'dosen.praoutline.index', + 'user' => $user, + 'usulan' => $usulan, + 'pageActions' => [ + ['label' => 'Pencarian Usulan', 'href' => route('dosen.praoutline.cari'), 'variant' => 'light'], + ], + ]); + } + + public function reviewSaya(Request $request): View + { + $user = $this->dosenUser($request); + + $reviews = DB::table('tbreview as tr') + ->join('tbpraoutline as tp', 'tp.id', '=', 'tr.idpraoutline') + ->leftJoin('tbmhs as tm', 'tm.nim', '=', 'tp.nim') + ->select([ + 'tp.id', + 'tp.judul', + 'tp.nim', + 'tm.nmLengkap', + 'tp.thn_ajaran', + 'tp.semester', + 'tp.tgl_upload', + 'tp.status_usulan', + ]) + ->where('tr.reviewer', $user['nip']) + ->distinct() + ->orderByDesc('tp.tgl_upload') + ->limit(30) + ->get() + ->map(fn ($item) => [ + 'id' => $item->id, + 'judul' => $item->judul, + 'mahasiswa' => ($item->nmLengkap ?: 'Mahasiswa tidak ditemukan').' ('.$item->nim.')', + 'periode' => trim(($item->thn_ajaran ?: '-').' / '.($item->semester ?: '-')), + 'tanggal' => $this->formatDateTime($item->tgl_upload), + 'status' => $this->statusUsulan($item->status_usulan), + 'reviewHref' => route('dosen.praoutline.review', $item->id, false), + ]) + ->all(); + + return $this->renderPage('dosen.pages.review-saya', [ + 'title' => 'Review Saya | SPOTA Rebuild', + 'pageTitle' => 'Review Saya', + 'pageDescription' => 'Usulan judul TA yang pernah diberi komentar atau tanggapan oleh dosen ini.', + 'routeName' => 'dosen.praoutline.review-saya', + 'user' => $user, + 'reviews' => $reviews, + ]); + } + + public function cari(Request $request): View + { + $user = $this->dosenUser($request); + $keyword = trim((string) $request->query('q', '')); + $by = (string) $request->query('by', 'judul'); + + $results = []; + + if ($keyword !== '') { + $query = DB::table('tbpraoutline as tp') + ->join('tbmhs as tm', 'tm.nim', '=', 'tp.nim') + ->leftJoin('tbrekaphasil as trh', 'trh.idpraoutline', '=', 'tp.id') + ->select([ + 'tp.id', + 'tp.judul', + 'tp.deskripsi', + 'tp.nim', + 'tm.nmLengkap', + 'tp.tgl_upload', + 'tp.status_usulan', + 'trh.judul_final', + 'trh.pemb1', + 'trh.pemb2', + 'trh.peng1', + 'trh.peng2', + ]); + + if ($by === 'nim') { + $query->where('tp.nim', 'like', '%'.$keyword.'%'); + } elseif ($by === 'dosen') { + $query->where(function ($builder) use ($keyword) { + $builder->where('trh.pemb1', 'like', '%'.$keyword.'%') + ->orWhere('trh.pemb2', 'like', '%'.$keyword.'%') + ->orWhere('trh.peng1', 'like', '%'.$keyword.'%') + ->orWhere('trh.peng2', 'like', '%'.$keyword.'%'); + }); + } else { + $query->where('tp.judul', 'like', '%'.$keyword.'%'); + } + + $results = $query + ->orderByDesc('tp.tgl_upload') + ->limit(20) + ->get() + ->map(fn ($item) => [ + 'id' => $item->id, + 'judul' => $item->judul, + 'mahasiswa' => $item->nmLengkap.' ('.$item->nim.')', + 'deskripsi' => $item->deskripsi ? mb_strimwidth(strip_tags($item->deskripsi), 0, 200, '...') : '-', + 'tanggal' => $this->formatDateTime($item->tgl_upload), + 'status' => $this->statusUsulan($item->status_usulan), + 'reviewHref' => route('dosen.praoutline.review', $item->id, false), + ]) + ->all(); + } + + return $this->renderPage('dosen.pages.cari', [ + 'title' => 'Pencarian Usulan | SPOTA Rebuild', + 'pageTitle' => 'Pencarian Usulan', + 'pageDescription' => 'Cari usulan berdasarkan NIM, judul, atau keterkaitan pembimbing dan penguji.', + 'routeName' => 'dosen.praoutline.cari', + 'user' => $user, + 'keyword' => $keyword, + 'searchBy' => $by, + 'results' => $results, + ]); + } + + public function bimbingan(Request $request): View + { + $user = $this->dosenUser($request); + + $bimbingan = DB::table('tbrekaphasil as trh') + ->leftJoin('tbmhs as tm', 'tm.nim', '=', 'trh.nim') + ->select([ + 'trh.id', + 'trh.idpraoutline', + 'trh.judul_final', + 'trh.nim', + 'tm.nmLengkap', + 'trh.tahun_ajaran', + 'trh.semester', + 'trh.tgl_kep', + ]) + ->where('trh.idProdi', $user['prodi']) + ->where('trh.kep_akhir', '1') + ->where(function ($query) use ($user) { + $query->where('trh.pemb1', $user['nip']) + ->orWhere('trh.pemb2', $user['nip']) + ->orWhere('trh.peng1', $user['nip']) + ->orWhere('trh.peng2', $user['nip']); + }) + ->orderByDesc('trh.tgl_kep') + ->limit(30) + ->get() + ->map(fn ($item) => [ + 'id' => $item->id, + 'judul' => $item->judul_final, + 'mahasiswa' => ($item->nmLengkap ?: 'Mahasiswa tidak ditemukan').' ('.$item->nim.')', + 'periode' => trim(($item->tahun_ajaran ?: '-').' / '.($item->semester ?: '-')), + 'tanggal' => $this->formatDateTime($item->tgl_kep), + 'reviewHref' => route('dosen.praoutline.review', $item->idpraoutline, false), + ]) + ->all(); + + return $this->renderPage('dosen.pages.bimbingan', [ + 'title' => 'Daftar Bimbingan Saya | SPOTA Rebuild', + 'pageTitle' => 'Daftar Bimbingan Saya', + 'pageDescription' => 'Mahasiswa yang telah ditetapkan pada dosen ini sebagai pembimbing atau penguji.', + 'routeName' => 'dosen.praoutline.bimbingan', + 'user' => $user, + 'bimbingan' => $bimbingan, + ]); + } + + public function statistik(Request $request): View + { + $user = $this->dosenUser($request); + + $draftStats = DB::table('tbpraoutline') + ->selectRaw('semester') + ->selectRaw("COUNT(IF(status_usulan='0',1,NULL)) as proses") + ->selectRaw("COUNT(IF(status_usulan='1',1,NULL)) as terima") + ->selectRaw("COUNT(IF(status_usulan='2',1,NULL)) as tolak") + ->selectRaw("COUNT(IF(status_usulan='3',1,NULL)) as gugur") + ->selectRaw('COUNT(*) as totaldraft') + ->where('idProdi', $user['prodi']) + ->groupBy('semester') + ->orderByDesc('semester') + ->get(); + + $dosenStats = DB::table('tbrekaphasil') + ->selectRaw("COUNT(IF(pemb1 = ?, 1, NULL)) as pemb1", [$user['nip']]) + ->selectRaw("COUNT(IF(pemb2 = ?, 1, NULL)) as pemb2", [$user['nip']]) + ->selectRaw("COUNT(IF(peng1 = ?, 1, NULL)) as peng1", [$user['nip']]) + ->selectRaw("COUNT(IF(peng2 = ?, 1, NULL)) as peng2", [$user['nip']]) + ->where('idProdi', $user['prodi']) + ->first(); + + return $this->renderPage('dosen.pages.statistik', [ + 'title' => 'Statistik Usulan | SPOTA Rebuild', + 'pageTitle' => 'Statistik Usulan', + 'pageDescription' => 'Ringkasan statistik draft praoutline dan peran dosen pada data rekap aktif.', + 'routeName' => 'dosen.praoutline.statistik', + 'user' => $user, + 'draftStats' => $draftStats, + 'dosenStats' => $dosenStats, + ]); + } + + public function pemberitahuan(Request $request): View + { + $user = $this->dosenUser($request); + + $pemberitahuan = DB::table('tmp_notif_r as tnr') + ->leftJoin('tbpraoutline as tp', 'tp.id', '=', 'tnr.idkonten') + ->select(['tnr.msg', 'tnr.tgl', 'tnr.idkonten', 'tp.judul']) + ->where('tnr.read', 'N') + ->where('tnr.jns_usr', 'D') + ->where('tnr.user', $user['nip']) + ->where('tnr.idProdi', $user['prodi']) + ->orderByDesc('tnr.tgl') + ->limit(30) + ->get() + ->map(fn ($item) => [ + 'msg' => $item->msg, + 'tgl' => $item->tgl, + 'title' => $item->judul, + 'reviewHref' => $item->idkonten ? route('dosen.praoutline.review', $item->idkonten, false).'#post_review' : null, + ]); + + return $this->renderPage('dosen.pages.pemberitahuan', [ + 'title' => 'Pemberitahuan | SPOTA Rebuild', + 'pageTitle' => 'Pemberitahuan', + 'pageDescription' => 'Daftar tanggapan dan review baru yang belum dibaca oleh dosen ini.', + 'routeName' => 'dosen.praoutline.pemberitahuan', + 'user' => $user, + 'pemberitahuan' => $pemberitahuan, + ]); + } + + public function pengumuman(Request $request): View + { + $user = $this->dosenUser($request); + + $pengumuman = DB::table('tbpengumuman as tp') + ->select([ + 'tp.id', + 'tp.judul', + 'tp.isi', + 'tp.tgl', + ]) + ->where('tp.idProdi', $user['prodi']) + ->whereIn('tp.tujuan', ['A', 'D']) + ->orderByDesc('tp.tgl') + ->limit(30) + ->get(); + + $pengumuman = $pengumuman->map(fn ($item) => [ + 'id' => $item->id, + 'judul' => $item->judul, + 'tgl' => $item->tgl, + 'detailHref' => route('dosen.pengumuman.show', $item->id), + ]); + + return $this->renderPage('dosen.pages.pengumuman', [ + 'title' => 'Pengumuman | SPOTA Rebuild', + 'pageTitle' => 'Daftar Pengumuman', + 'pageDescription' => 'Pengumuman program studi yang ditujukan untuk dosen dan akses umum.', + 'routeName' => 'dosen.pengumuman.index', + 'user' => $user, + 'pengumuman' => $pengumuman, + ]); + } + + public function reviewDetail(Request $request, int $id): View + { + $user = $this->dosenUser($request); + + $outline = DB::table('tbpraoutline as tp') + ->leftJoin('tbmhs as tm', 'tm.nim', '=', 'tp.nim') + ->leftJoin('tb_kelompok_keahlian as tkk', 'tkk.idKK', '=', 'tp.kelompokKeahlian') + ->select([ + 'tp.id', + 'tp.judul', + 'tp.deskripsi', + 'tp.nim', + 'tp.thn_ajaran', + 'tp.semester', + 'tp.tgl_upload', + 'tp.wkt_upload', + 'tp.status_usulan', + 'tm.nmLengkap', + 'tkk.namaKK', + ]) + ->where('tp.id', $id) + ->where('tp.idProdi', $user['prodi']) + ->first(); + + abort_unless($outline, 404); + + $reviews = DB::table('tbreview as tr') + ->leftJoin('tbdosen as td', 'td.nip', '=', 'tr.reviewer') + ->leftJoin('tbmhs as tm', 'tm.nim', '=', 'tr.reviewer') + ->select([ + 'tr.id', + 'tr.review_text', + 'tr.jenis_review', + 'tr.putusan', + 'tr.tgl', + 'tr.wkt', + 'tr.reviewer', + 'td.nmLengkap as namaDosen', + 'tm.nmLengkap as namaMahasiswa', + ]) + ->where('tr.idpraoutline', $outline->id) + ->orderBy('tr.tgl') + ->orderBy('tr.wkt') + ->orderBy('tr.id') + ->get() + ->map(function ($item) { + $isDecision = (string) $item->jenis_review === '1'; + + return [ + 'id' => $item->id, + 'author' => $item->namaDosen ?: ($item->namaMahasiswa ?: $item->reviewer), + 'role' => $item->namaDosen ? 'Dosen' : 'Mahasiswa', + 'timestamp' => $this->formatDateTime(trim(($item->tgl ?: '').' '.($item->wkt ?: ''))), + 'body' => $item->review_text, + 'type' => $isDecision ? 'Putusan' : 'Komentar', + 'decision' => ! $isDecision ? null : ((string) $item->putusan === '1' ? 'Setuju' : 'Tidak Setuju'), + ]; + }) + ->all(); + + return $this->renderPage('dosen.pages.review-detail', [ + 'title' => 'Detail Review | SPOTA Rebuild', + 'pageTitle' => 'Detail Review Usulan', + 'pageDescription' => 'Riwayat komentar dan putusan pada usulan judul mahasiswa.', + 'routeName' => 'dosen.praoutline.review', + 'user' => $user, + 'outline' => [ + 'id' => $outline->id, + 'judul' => $outline->judul, + 'deskripsi' => $outline->deskripsi, + 'mahasiswa' => ($outline->nmLengkap ?: 'Mahasiswa tidak ditemukan').' ('.$outline->nim.')', + 'periode' => trim(($outline->thn_ajaran ?: '-').' / '.($outline->semester ?: '-')), + 'tanggal' => $this->formatDateTime(trim(($outline->tgl_upload ?: '').' '.($outline->wkt_upload ?: ''))), + 'status' => $this->statusUsulan($outline->status_usulan), + 'kk' => $outline->namaKK ?: '-', + ], + 'reviews' => $reviews, + 'pageActions' => [ + ['label' => 'Kembali ke Review Saya', 'href' => route('dosen.praoutline.review-saya', [], false), 'variant' => 'light'], + ], + ]); + } + + public function showPengumuman(Request $request, int $id): View + { + $user = $this->dosenUser($request); + + $pengumuman = DB::table('tbpengumuman') + ->where('id', $id) + ->where('idProdi', $user['prodi']) + ->whereIn('tujuan', ['A', 'D']) + ->first(); + + abort_unless($pengumuman, 404); + + $alreadyRead = DB::table('tmp_notif') + ->where('idkonten', $id) + ->where('iduser', $user['id']) + ->where('typeuser', 'D') + ->where('jenis', 'P') + ->exists(); + + if (! $alreadyRead) { + DB::table('tmp_notif')->insert([ + 'idkonten' => $id, + 'idProdi' => $user['prodi'], + 'iduser' => $user['id'], + 'typeuser' => 'D', + 'date' => now(), + 'jenis' => 'P', + ]); + } + + return $this->renderPage('dosen.pages.pengumuman-detail', [ + 'title' => 'Lihat Pengumuman | SPOTA Rebuild', + 'pageTitle' => 'Lihat Pengumuman', + 'pageDescription' => 'Detail pengumuman dosen dari sistem SPOTA lama.', + 'routeName' => 'dosen.pengumuman.show', + 'user' => $user, + 'pengumumanItem' => $pengumuman, + 'pageActions' => [ + ['label' => 'Kembali ke Pengumuman', 'href' => route('dosen.pengumuman.index'), 'variant' => 'light'], + ], + ]); + } + + public function profile(Request $request): View + { + $user = $this->dosenUser($request); + + $profile = DB::table('tbdosen') + ->where('iddosen', $user['id']) + ->first(); + + return $this->renderPage('dosen.pages.profile', [ + 'title' => 'Profil Dosen | SPOTA Rebuild', + 'pageTitle' => 'Profil Dosen', + 'pageDescription' => 'Kelola data akun dasar dosen yang aktif pada SPOTA.', + 'routeName' => 'dosen.profile', + 'user' => $user, + 'profile' => $profile, + ]); + } + + public function updateProfile(Request $request): RedirectResponse + { + $user = $this->dosenUser($request); + + $data = $request->validate([ + 'nmLengkap' => ['required', 'string', 'max:150'], + 'email' => ['nullable', 'email', 'max:150'], + 'nohp' => ['nullable', 'string', 'max:75'], + 'password' => ['nullable', 'string', 'min:6', 'max:75', 'same:password_again'], + 'password_again' => ['nullable', 'string', 'min:6', 'max:75'], + ], [ + 'password.same' => 'Konfirmasi password tidak sesuai.', + ]); + + $payload = [ + 'nmLengkap' => trim($data['nmLengkap']), + 'email' => trim((string) ($data['email'] ?? '')), + 'nohp' => trim((string) ($data['nohp'] ?? '')), + ]; + + if (! empty($data['password'])) { + $payload['password'] = md5($data['password']); + } + + DB::table('tbdosen') + ->where('iddosen', $user['id']) + ->update($payload); + + $request->session()->put('legacy_auth.user', array_merge($user, [ + 'nama_lengkap' => $payload['nmLengkap'], + ])); + + return redirect()->route('dosen.profile')->with('success', 'Profil dosen berhasil diperbarui.'); + } + + public function earlyWarning(Request $request): View + { + $user = $this->dosenUser($request); + + $records = DB::table('tbrekaphasil as trh') + ->leftJoin('tbmhs as tm', 'tm.nim', '=', 'trh.nim') + ->select([ + 'trh.nim', + 'tm.nmLengkap', + 'tm.thnmasuk', + 'trh.judul_final', + 'trh.tgl_kep', + ]) + ->where('trh.idProdi', $user['prodi']) + ->where(function ($query) use ($user) { + $query->where('trh.pemb1', $user['nip']) + ->orWhere('trh.pemb2', $user['nip']) + ->orWhere('trh.peng1', $user['nip']) + ->orWhere('trh.peng2', $user['nip']); + }) + ->orderBy('trh.tgl_kep') + ->limit(50) + ->get() + ->map(function ($item) { + $days = $item->tgl_kep ? Carbon::parse($item->tgl_kep)->diffInDays(now()) : null; + $angkatan = $this->resolveAngkatan($item->nim, $item->thnmasuk ?? null); + $tahunStudi = $angkatan ? (int) now()->format('Y') - $angkatan : null; + + if ($tahunStudi === null) { + $severity = 'unknown'; + $status = 'Angkatan Tidak Diketahui'; + } elseif ($tahunStudi >= 8) { + $severity = 'dropout'; + $status = 'Ancaman DO'; + } elseif ($tahunStudi >= 7) { + $severity = 'critical'; + $status = 'Kritis Akhir Studi'; + } elseif ($tahunStudi >= 6) { + $severity = 'warning'; + $status = 'Warning'; + } elseif ($tahunStudi >= 5) { + $severity = 'watch'; + $status = 'Perlu Pantau'; + } else { + $severity = 'safe'; + $status = 'Aman'; + } + + return [ + 'mahasiswa' => ($item->nmLengkap ?: 'Mahasiswa tidak ditemukan').' ('.$item->nim.')', + 'nim' => $item->nim, + 'judul' => $item->judul_final ?: '-', + 'tanggal' => $this->formatDateTime($item->tgl_kep), + 'status' => $status, + 'days' => $days, + 'angkatan' => $angkatan, + 'tahunStudi' => $tahunStudi, + 'severity' => $severity, + 'statusClass' => match ($severity) { + 'dropout' => 'bg-[#3B0A0A] text-white border border-[#3B0A0A]', + 'critical' => 'bg-[#7F1D1D] text-white border border-[#7F1D1D]', + 'warning' => 'bg-rose-100 text-rose-700 border border-rose-200', + 'watch' => 'bg-amber-100 text-amber-700 border border-amber-200', + 'safe' => 'bg-emerald-100 text-emerald-700 border border-emerald-200', + default => 'bg-slate-100 text-slate-700 border border-slate-200', + }, + 'warningText' => match ($severity) { + 'dropout' => 'Mahasiswa sudah masuk ambang akhir masa studi dan perlu penanganan prioritas tertinggi.', + 'critical' => 'Mahasiswa berada di tahun akhir masa studi dan berisiko tinggi jika progres tersendat.', + 'warning' => 'Mahasiswa sudah melewati fase aman studi dan perlu tindak lanjut aktif.', + 'watch' => 'Mahasiswa memasuki tahun studi lanjut, sebaiknya dipantau lebih ketat.', + 'safe' => 'Mahasiswa masih dalam rentang studi yang relatif aman untuk monitoring rutin.', + default => 'Angkatan mahasiswa belum dapat ditentukan dari data yang tersedia.', + }, + 'detailHref' => $this->legacyUrl('dosen/dashboard.php?page=early-warning'), + ]; + }) + ->all(); + + $dropoutCount = collect($records)->where('severity', 'dropout')->count(); + $criticalCount = collect($records)->where('severity', 'critical')->count(); + $warningCount = collect($records)->where('severity', 'warning')->count(); + $watchCount = collect($records)->where('severity', 'watch')->count(); + $safeCount = collect($records)->where('severity', 'safe')->count(); + + return $this->renderPage('dosen.pages.early-warning', [ + 'title' => 'Early Warning | SPOTA Rebuild', + 'pageTitle' => 'Early Warning', + 'pageDescription' => 'Pantauan mahasiswa bimbingan berdasarkan tanggal keputusan outline yang tersimpan.', + 'routeName' => 'dosen.early-warning', + 'user' => $user, + 'records' => $records, + 'summary' => [ + 'dropoutCount' => $dropoutCount, + 'criticalCount' => $criticalCount, + 'warningCount' => $warningCount, + 'watchCount' => $watchCount, + 'safeCount' => $safeCount, + 'totalCount' => count($records), + ], + ]); + } + + public function praLirs(Request $request): View + { + $user = $this->dosenUser($request); + + return $this->renderPage('dosen.pages.pra-lirs', [ + 'title' => 'Pra LIRS | SPOTA Rebuild', + 'pageTitle' => 'Pra LIRS (Dosen PA)', + 'pageDescription' => 'Integrasi Pra LIRS saat ini masih memakai sumber data eksternal yang sama dengan modul lama.', + 'routeName' => 'dosen.pra-lirs', + 'user' => $user, + 'externalUrl' => 'https://informatika.untan.ac.id/API/public/getListMahasiswaPralirsPASaya.php?nip='.$user['nip'], + ]); + } + + private function renderPage(string $view, array $data): View + { + $data['sidebar'] = DosenNavigation::build($data['routeName']); + $data['pageDate'] = $this->formatDateTime(now()->toDateTimeString()); + + return view($view, $data); + } + + private function dosenUser(Request $request): array + { + $auth = $request->session()->get('legacy_auth'); + + abort_unless(($auth['role'] ?? null) === 'dosen', 403); + + return $auth['user']; + } + + private function formatDateTime(?string $value): string + { + if (! $value || strtotime($value) === false) { + return '-'; + } + + return Carbon::parse($value)->locale('id')->translatedFormat('j F Y, H:i'); + } + + private function statusPenawaran($status): string + { + return match ((string) $status) { + '0' => 'Belum Diproses', + '1' => 'Diterima', + '2' => 'Belum Diambil', + default => 'Belum Diambil', + }; + } + + private function statusUsulan($status): string + { + return match ((string) $status) { + '1' => 'Judul Diterima', + '2' => 'Judul Ditolak', + '3' => 'Judul Gugur', + default => 'Dalam Proses', + }; + } + + private function latestPenawaranBooking(int $idPenawaran, string $idDosen): ?object + { + return DB::table('tb_ambil_judul as taj') + ->join('tb_penawaran_judul as tpj', 'tpj.idPenawaran', '=', 'taj.idPenawaranAmbil') + ->select(['taj.idAmbil', 'taj.statusPengambilan']) + ->where('taj.idPenawaranAmbil', $idPenawaran) + ->where('tpj.idDosen', $idDosen) + ->orderByDesc('taj.waktuPengambilan') + ->first(); + } + + private function resolveAngkatan(?string $nim, $thnMasuk): ?int + { + $thnMasuk = is_numeric($thnMasuk) ? (int) $thnMasuk : null; + if ($thnMasuk && $thnMasuk > 2000 && $thnMasuk <= ((int) date('Y') + 1)) { + return $thnMasuk; + } + + $nim = (string) $nim; + if (strlen($nim) >= 7 && preg_match('/^(?:[A-Z])(\d{6,})/i', $nim, $matches)) { + $digits = $matches[1]; + $angkatan2Digit = substr($digits, 4, 2); + if (ctype_digit($angkatan2Digit)) { + $year = 2000 + (int) $angkatan2Digit; + if ($year <= ((int) date('Y') + 1)) { + return $year; + } + } + } + + return null; + } + + private function legacyUrl(string $path): string + { + $baseUrl = rtrim((string) env('LEGACY_BASE_URL', 'http://127.0.0.1:8080'), '/'); + + return $baseUrl.'/'.ltrim($path, '/'); + } +} diff --git a/rebuild/app/Http/Controllers/MahasiswaPageController.php b/rebuild/app/Http/Controllers/MahasiswaPageController.php new file mode 100644 index 0000000..0a6de4f --- /dev/null +++ b/rebuild/app/Http/Controllers/MahasiswaPageController.php @@ -0,0 +1,467 @@ +getMahasiswaUser($request); + + $outline = DB::table('tbpraoutline as tp') + ->leftJoin('tbrekaphasil as trh', 'tp.id', '=', 'trh.idpraoutline') + ->leftJoin('tb_kelompok_keahlian as kk', 'tp.kelompokKeahlian', '=', 'kk.idKK') + ->select([ + 'tp.id', + 'tp.judul', + 'tp.deskripsi', + 'tp.status_usulan', + 'tp.tgl_upload', + 'tp.wkt_upload', + 'trh.ket', + 'kk.namaKK as kelompok_keahlian', + ]) + ->where('tp.nim', $user['nim']) + ->where('tp.idProdi', $user['prodi']) + ->orderByDesc('tp.id') + ->first(); + + $reviews = []; + + if ($outline) { + $reviews = DB::table('tbreview as tr') + ->leftJoin('tbdosen as td', 'tr.reviewer', '=', 'td.nip') + ->leftJoin('tbmhs as tm', 'tr.reviewer', '=', 'tm.nim') + ->select([ + 'tr.review_text', + 'tr.jenis_review', + 'tr.putusan', + 'tr.tgl', + 'tr.wkt', + 'tr.reviewer', + 'td.nmLengkap as reviewer_name', + 'tm.nmLengkap as mahasiswa_name', + ]) + ->where('tr.idpraoutline', $outline->id) + ->orderBy('tr.tgl') + ->orderBy('tr.wkt') + ->get() + ->map(function ($item) { + $timestamp = trim(($item->tgl ?? '').' '.($item->wkt ?? '')); + + return [ + 'author' => $item->reviewer_name ?: ($item->mahasiswa_name ?: $item->reviewer), + 'role' => $item->reviewer_name ? 'Dosen' : 'Mahasiswa', + 'timestamp' => $this->formatDateTime($timestamp), + 'type' => $item->jenis_review === 'P' ? 'Putusan' : 'Review', + 'decision' => $this->decisionLabel($item->putusan), + 'body' => $item->review_text ?: '

Tidak ada isi review.

', + ]; + }) + ->all(); + } + + return view('mahasiswa.pages.status-usulan', [ + 'title' => 'Status Usulan Mahasiswa | SPOTA Rebuild', + 'pageTitle' => 'Status Usulan', + 'pageDescription' => 'Ringkasan draft praoutline terakhir, status keputusan, dan riwayat review dosen untuk mahasiswa yang sedang login.', + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => $this->buildSidebar('mahasiswa.status-usulan'), + 'user' => $user, + 'outline' => $outline ? [ + 'id' => $outline->id, + 'judul' => $outline->judul, + 'deskripsi' => $outline->deskripsi, + 'status' => $this->statusLabel($outline->status_usulan), + 'statusClass' => $this->statusBadgeClass($outline->status_usulan), + 'tanggal' => $this->formatDateTime(trim(($outline->tgl_upload ?? '').' '.($outline->wkt_upload ?? ''))), + 'catatan' => $outline->ket, + 'kelompokKeahlian' => $outline->kelompok_keahlian ?: '-', + ] : null, + 'reviews' => $reviews, + 'pageActions' => $this->statusPageActions($outline), + ]); + } + + public function uploadPraoutline(Request $request): View + { + $user = $this->getMahasiswaUser($request); + + $hasActiveDraft = DB::table('tbpraoutline') + ->where('nim', $user['nim']) + ->whereIn('status_usulan', ['0', '1']) + ->exists(); + + $dosen = DB::table('tbdosen') + ->select(['nmLengkap']) + ->where('idProdi', $user['prodi']) + ->where('status', 'A') + ->orderBy('nmLengkap') + ->get(); + + $kelompokKeahlian = DB::table('tb_kelompok_keahlian') + ->select(['idKK', 'namaKK']) + ->orderBy('namaKK') + ->get(); + + return view('mahasiswa.pages.upload-praoutline', [ + 'title' => 'Ajukan Praoutline | SPOTA Rebuild', + 'pageTitle' => 'Ajukan Outline Baru', + 'pageDescription' => 'Form pengajuan draft praoutline mengikuti field utama pada SPOTA lama. File PDF tetap divalidasi sebelum disimpan.', + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => $this->buildSidebar('mahasiswa.praoutline.upload'), + 'user' => $user, + 'hasActiveDraft' => $hasActiveDraft, + 'dosen' => $dosen, + 'kelompokKeahlian' => $kelompokKeahlian, + ]); + } + + public function storePraoutline(Request $request): RedirectResponse + { + $user = $this->getMahasiswaUser($request); + + $hasActiveDraft = DB::table('tbpraoutline') + ->where('nim', $user['nim']) + ->whereIn('status_usulan', ['0', '1']) + ->exists(); + + if ($hasActiveDraft) { + return back()->with('error', 'Draft praoutline aktif masih ada. Silakan lihat status usulan/review terlebih dahulu.'); + } + + $validated = $request->validate([ + 'judul' => ['required', 'string', 'max:255'], + 'deskripsi' => ['nullable', 'string'], + 'berkas' => ['required', 'file', 'mimes:pdf', 'max:10240'], + 'dosenpa' => ['required', 'string', 'max:255'], + 'pilpemb1' => ['nullable', 'string', 'max:255'], + 'pilpemb2' => ['nullable', 'string', 'max:255'], + 'pilpemb3' => ['nullable', 'string', 'max:255'], + 'pilpemb4' => ['nullable', 'string', 'max:255'], + 'drekomjudul' => ['nullable', 'string', 'max:255'], + 'kelompokKeahlian' => ['required', 'integer'], + ]); + + $file = $request->file('berkas'); + $filename = $user['nim'].'-'.time().'.'.$file->getClientOriginalExtension(); + $file->move(base_path('../files'), $filename); + + DB::table('tbpraoutline')->insert([ + 'nim' => $user['nim'], + 'judul' => $validated['judul'], + 'deskripsi' => $validated['deskripsi'] ?? '', + 'berkas' => $filename, + 'idProdi' => $user['prodi'], + 'tgl_upload' => Carbon::now()->toDateString(), + 'wkt_upload' => Carbon::now()->format('H:i:s'), + 'semester' => $this->currentSemester(), + 'thn_ajaran' => $this->currentAcademicYear(), + 'status_usulan' => '0', + 'ket' => '', + 'kelompokKeahlian' => (string) $validated['kelompokKeahlian'], + 'kkTerkait' => '', + ]); + + return redirect()->route('mahasiswa.status-usulan')->with('success', 'Draft praoutline berhasil diajukan.'); + } + + public function pengumuman(Request $request): View + { + $user = $this->getMahasiswaUser($request); + + $pengumuman = DB::table('tbpengumuman') + ->select(['id', 'judul', 'isi', 'tgl']) + ->where('idProdi', $user['prodi']) + ->whereIn('tujuan', ['A', 'M']) + ->orderByDesc('id') + ->limit(25) + ->get() + ->map(function ($item) { + return [ + 'id' => $item->id, + 'judul' => $item->judul, + 'preview' => str($item->isi)->stripTags()->squish()->limit(180)->toString(), + 'tgl' => $item->tgl, + 'detailHref' => route('mahasiswa.pengumuman.show', ['id' => $item->id], false), + ]; + }) + ->all(); + + return view('mahasiswa.pages.pengumuman', [ + 'title' => 'Pengumuman Mahasiswa | SPOTA Rebuild', + 'pageTitle' => 'Pengumuman', + 'pageDescription' => 'Daftar pengumuman program studi yang ditujukan untuk mahasiswa pada data SPOTA aktif.', + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => $this->buildSidebar('mahasiswa.pengumuman.index'), + 'user' => $user, + 'pengumuman' => $pengumuman, + ]); + } + + public function showPengumuman(Request $request, int $id): View + { + $user = $this->getMahasiswaUser($request); + + $pengumuman = DB::table('tbpengumuman') + ->select(['id', 'judul', 'isi', 'tgl']) + ->where('id', $id) + ->where('idProdi', $user['prodi']) + ->whereIn('tujuan', ['A', 'M']) + ->first(); + + abort_unless($pengumuman, 404); + + return view('mahasiswa.pages.pengumuman-detail', [ + 'title' => 'Detail Pengumuman Mahasiswa | SPOTA Rebuild', + 'pageTitle' => 'Detail Pengumuman', + 'pageDescription' => 'Isi pengumuman mahasiswa dari basis data SPOTA aktif.', + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => $this->buildSidebar('mahasiswa.pengumuman.index'), + 'user' => $user, + 'pageActions' => [ + ['label' => 'Kembali ke Pengumuman', 'href' => route('mahasiswa.pengumuman.index', [], false)], + ], + 'pengumuman' => [ + 'judul' => $pengumuman->judul, + 'isi' => $pengumuman->isi, + 'tgl' => $this->formatDateTime($pengumuman->tgl), + ], + ]); + } + + public function penawaran(Request $request): View + { + $user = $this->getMahasiswaUser($request); + $status = $request->query('status', '0'); + $kk = $request->query('kk', 'Semua'); + + $query = DB::table('tb_penawaran_judul as tpj') + ->leftJoin('tbdosen as td', 'tpj.idDosen', '=', 'td.iddosen') + ->leftJoin('tb_kelompok_keahlian as kk', 'tpj.kk', '=', 'kk.idKK') + ->leftJoin('tb_ambil_judul as taj', function ($join) { + $join->on('tpj.idPenawaran', '=', 'taj.idPenawaranAmbil') + ->whereRaw('taj.idAmbil = (select max(taj2.idAmbil) from tb_ambil_judul taj2 where taj2.idPenawaranAmbil = tpj.idPenawaran)'); + }) + ->leftJoin('tbmhs as tm', 'taj.idMhs', '=', 'tm.idmhs') + ->select([ + 'tpj.idPenawaran', + 'tpj.judul', + 'tpj.deskripsi', + 'tpj.waktuInput', + 'td.nmLengkap as dosen', + 'kk.namaKK as kk', + 'taj.statusPengambilan', + 'tm.nmLengkap as diambil_oleh', + ]) + ->orderByDesc('tpj.idPenawaran'); + + if ($kk !== 'Semua') { + $query->where('tpj.kk', $kk); + } + + if ($status === '0') { + $query->where(function ($query) { + $query->whereNull('taj.idAmbil')->orWhere('taj.statusPengambilan', '2'); + }); + } elseif ($status === '1') { + $query->whereNotNull('taj.idAmbil')->where('taj.statusPengambilan', '!=', '2'); + } + + $penawaran = $query->paginate(15)->withQueryString(); + $kelompokKeahlian = DB::table('tb_kelompok_keahlian') + ->select(['idKK', 'namaKK']) + ->where('idKK', '!=', '8') + ->orderBy('namaKK') + ->get(); + + return view('mahasiswa.pages.penawaran', [ + 'title' => 'Penawaran Judul Mahasiswa | SPOTA Rebuild', + 'pageTitle' => 'Penawaran Judul', + 'pageDescription' => 'Daftar judul yang ditawarkan dosen. Mahasiswa dapat melihat detail dan booking judul yang masih tersedia.', + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => $this->buildSidebar('mahasiswa.penawaran.index'), + 'user' => $user, + 'penawaran' => $penawaran, + 'kelompokKeahlian' => $kelompokKeahlian, + 'filters' => ['status' => $status, 'kk' => $kk], + ]); + } + + public function showPenawaran(Request $request, int $id): View + { + $user = $this->getMahasiswaUser($request); + + $penawaran = DB::table('tb_penawaran_judul as tpj') + ->leftJoin('tbdosen as td', 'tpj.idDosen', '=', 'td.iddosen') + ->leftJoin('tb_kelompok_keahlian as kk', 'tpj.kk', '=', 'kk.idKK') + ->select([ + 'tpj.idPenawaran', + 'tpj.judul', + 'tpj.deskripsi', + 'tpj.waktuInput', + 'td.nmLengkap as dosen', + 'kk.namaKK as kk', + ]) + ->where('tpj.idPenawaran', $id) + ->first(); + + abort_unless($penawaran, 404); + + return view('mahasiswa.pages.penawaran-detail', [ + 'title' => 'Detail Penawaran Judul | SPOTA Rebuild', + 'pageTitle' => 'Detail Penawaran Judul', + 'pageDescription' => 'Detail judul yang ditawarkan dosen dan tombol booking apabila mahasiswa ingin mengambil judul ini.', + 'pageDate' => Carbon::now()->locale('id')->translatedFormat('j F Y, H:i'), + 'sidebar' => $this->buildSidebar('mahasiswa.penawaran.index'), + 'user' => $user, + 'pageActions' => [ + ['label' => 'Kembali ke Penawaran', 'href' => route('mahasiswa.penawaran.index', [], false)], + ], + 'penawaran' => $penawaran, + ]); + } + + public function bookPenawaran(Request $request, int $id): RedirectResponse + { + $user = $this->getMahasiswaUser($request); + + $latestStudentBooking = DB::table('tb_ambil_judul') + ->where('idMhs', $user['id']) + ->orderByDesc('waktuPengambilan') + ->first(); + + if ($latestStudentBooking?->statusPengambilan === '0') { + return back()->with('error', 'Tidak dapat booking judul ini karena masih ada booking lain yang menunggu verifikasi dosen.'); + } + + if ($latestStudentBooking?->statusPengambilan === '1' && strtotime($latestStudentBooking->waktuPengambilan) >= strtotime('-30 days')) { + return back()->with('error', 'Tidak dapat booking judul ini karena booking sebelumnya sudah disetujui dosen.'); + } + + $latestTitleBooking = DB::table('tb_ambil_judul') + ->where('idPenawaranAmbil', $id) + ->orderByDesc('waktuPengambilan') + ->first(); + + if ($latestTitleBooking && $latestTitleBooking->statusPengambilan !== '2') { + return back()->with('error', 'Judul ini sudah dibooking mahasiswa lain.'); + } + + DB::table('tb_ambil_judul')->insert([ + 'idPenawaranAmbil' => $id, + 'idMhs' => $user['id'], + 'statusPengambilan' => '0', + 'waktuPengambilan' => Carbon::now()->toDateTimeString(), + ]); + + return redirect()->route('mahasiswa.penawaran.index')->with('success', 'Berhasil membooking judul ini. Tunggu verifikasi dari dosen penawar.'); + } + + private function getMahasiswaUser(Request $request): array + { + $auth = $request->session()->get('legacy_auth'); + + abort_unless(($auth['role'] ?? null) === 'mahasiswa', 403); + + return $auth['user']; + } + + private function buildSidebar(string $activeRoute): array + { + return [ + 'main' => [ + ['title' => 'Dashboard', 'href' => route('dashboard.mahasiswa'), 'icon' => 'home', 'active' => $activeRoute === 'dashboard.mahasiswa'], + ], + 'sections' => [ + [ + 'title' => 'Praoutline', + 'icon' => 'folder', + 'items' => [ + ['title' => 'Status Usulan', 'href' => route('mahasiswa.status-usulan', [], false), 'icon' => 'chart', 'active' => $activeRoute === 'mahasiswa.status-usulan'], + ['title' => 'Ajukan Outline Baru', 'href' => route('mahasiswa.praoutline.upload', [], false), 'icon' => 'clipboard', 'active' => $activeRoute === 'mahasiswa.praoutline.upload'], + ['title' => 'Penawaran Judul', 'href' => route('mahasiswa.penawaran.index', [], false), 'icon' => 'briefcase', 'active' => $activeRoute === 'mahasiswa.penawaran.index'], + ], + ], + [ + 'title' => 'Informasi', + 'icon' => 'bell', + 'items' => [ + ['title' => 'Pengumuman', 'href' => route('mahasiswa.pengumuman.index', [], false), 'icon' => 'megaphone', 'active' => $activeRoute === 'mahasiswa.pengumuman.index'], + ], + ], + ], + ]; + } + + private function formatDateTime(?string $value): string + { + if (! $value) { + return '-'; + } + + return Carbon::parse($value)->locale('id')->translatedFormat('j F Y, H:i'); + } + + private function statusLabel(?string $status): string + { + return match ($status) { + '0' => 'Dalam Review', + '1' => 'Disetujui', + '2' => 'Ditolak', + default => 'Belum Ada Draft', + }; + } + + private function statusBadgeClass(?string $status): string + { + return match ($status) { + '0' => 'bg-sky-100 text-sky-800', + '1' => 'bg-emerald-100 text-emerald-800', + '2' => 'bg-rose-100 text-rose-800', + default => 'bg-slate-100 text-slate-700', + }; + } + + private function decisionLabel(?string $status): ?string + { + return match ($status) { + '1' => 'Setuju', + '0' => 'Tidak Setuju', + '2' => 'Tolak', + default => null, + }; + } + + private function statusPageActions(?object $outline): array + { + $actions = [ + ['label' => 'Ajukan Outline Baru', 'href' => route('mahasiswa.praoutline.upload', [], false), 'variant' => ! $outline || $outline->status_usulan === '2' ? 'dark' : 'light'], + ['label' => 'Lihat Penawaran Judul', 'href' => route('mahasiswa.penawaran.index', [], false)], + ]; + + return $actions; + } + + private function currentSemester(): string + { + return (int) date('n') >= 8 || (int) date('n') <= 1 ? 'Ganjil' : 'Genap'; + } + + private function currentAcademicYear(): string + { + $year = (int) date('Y'); + + if ((int) date('n') >= 8) { + return $year.'/'.($year + 1); + } + + return ($year - 1).'/'.$year; + } +} diff --git a/rebuild/app/Http/Middleware/EnsureLegacyRole.php b/rebuild/app/Http/Middleware/EnsureLegacyRole.php new file mode 100644 index 0000000..f93bc37 --- /dev/null +++ b/rebuild/app/Http/Middleware/EnsureLegacyRole.php @@ -0,0 +1,22 @@ +session()->get('legacy_auth'); + + if (! $auth || ($auth['role'] ?? null) !== $role) { + return redirect()->route('legacy.login', $role) + ->withErrors(['identifier' => 'Silakan login terlebih dahulu untuk mengakses dashboard ini.']); + } + + return $next($request); + } +} diff --git a/rebuild/app/Models/User.php b/rebuild/app/Models/User.php new file mode 100644 index 0000000..f6ba1d2 --- /dev/null +++ b/rebuild/app/Models/User.php @@ -0,0 +1,32 @@ + */ + use HasFactory, Notifiable; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; + } +} diff --git a/rebuild/app/Providers/AppServiceProvider.php b/rebuild/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..452e6b6 --- /dev/null +++ b/rebuild/app/Providers/AppServiceProvider.php @@ -0,0 +1,24 @@ + 'Manajemen Data', + 'icon' => 'folder', + 'items' => $isSuper ? [ + self::page('Data Fakultas', 'admin.data.fakultas', 'document'), + self::page('Data Jurusan', 'admin.data.jurusan', 'document'), + self::page('Data Program Studi', 'admin.data.prodi', 'document'), + ] : [ + self::page('Data Mahasiswa', 'admin.data.mahasiswa', 'users'), + self::page('Data Dosen', 'admin.data.dosen', 'users'), + self::page('Data Kelompok Keahlian', 'admin.data.kk', 'folder'), + ], + ], + [ + 'title' => 'User', + 'icon' => 'user', + 'items' => array_values(array_filter([ + self::page('Profil Saya', 'admin.profile', 'user'), + $isSuper ? self::page('Manajemen Admin', 'admin.users', 'users') : null, + ])), + ], + [ + 'title' => 'Lainnya', + 'icon' => 'briefcase', + 'items' => [ + ['title' => 'Dokumen Sidang', 'href' => 'https://edoxid.untan.ac.id/', 'icon' => 'document', 'external' => true], + ], + ], + ]; + + if (! $isSuper) { + array_splice($sections, 1, 0, [[ + 'title' => 'Praoutline', + 'icon' => 'clipboard', + 'items' => [ + self::page('Daftar Draft Praoutline', 'admin.praoutline.index', 'document'), + self::page('Pencarian', 'admin.praoutline.search', 'search'), + self::page('Kep. Penunjukan Dosen', 'admin.praoutline.keputusan', 'clipboard'), + self::page('Kep. Draft Praoutline', 'admin.praoutline.kep-draft', 'clipboard'), + self::page('Pemberitahuan', 'admin.praoutline.pemberitahuan', 'bell'), + ], + ], [ + 'title' => 'Pengumuman', + 'icon' => 'megaphone', + 'items' => [ + self::page('Daftar Pengumuman', 'admin.pengumuman.index', 'megaphone'), + self::page('Buat Pengumuman Baru', 'admin.pengumuman.create', 'document'), + ], + ], [ + 'title' => 'Jadwal Seminar/Sidang', + 'icon' => 'clock', + 'items' => [ + self::page('Manajemen Data', 'admin.jadwal.index', 'document'), + self::page('Kalender', 'admin.jadwal.kalender', 'clock'), + ], + ], [ + 'title' => 'Pengaturan', + 'icon' => 'warning', + 'items' => [ + self::page('Pengaturan Prodi', 'admin.pengaturan', 'warning'), + ], + ]]); + } + + return [ + 'main' => [ + ['title' => 'Dashboard', 'href' => route('dashboard.admin'), 'icon' => 'home', 'active' => true], + ], + 'sections' => $sections, + ]; + } + + private static function page(string $title, string $route, string $icon): array + { + return [ + 'title' => $title, + 'href' => route($route, [], false), + 'icon' => $icon, + ]; + } +} diff --git a/rebuild/app/Support/DosenNavigation.php b/rebuild/app/Support/DosenNavigation.php new file mode 100644 index 0000000..76dd942 --- /dev/null +++ b/rebuild/app/Support/DosenNavigation.php @@ -0,0 +1,51 @@ + [ + ['title' => 'Dashboard', 'href' => route('dashboard.dosen'), 'icon' => 'home', 'active' => $activeRoute === 'dashboard.dosen'], + ], + 'sections' => [ + [ + 'title' => 'Utama', + 'icon' => 'briefcase', + 'items' => [ + ['title' => 'Penawaran Judul', 'href' => route('dosen.penawaran.index'), 'icon' => 'briefcase', 'active' => $activeRoute === 'dosen.penawaran.index'], + ], + ], + [ + 'title' => 'Tugas Akhir 1', + 'icon' => 'folder', + 'items' => [ + ['title' => 'Daftar Usulan', 'href' => route('dosen.praoutline.index'), 'icon' => 'folder', 'active' => $activeRoute === 'dosen.praoutline.index'], + ['title' => 'Review Saya', 'href' => route('dosen.praoutline.review-saya'), 'icon' => 'chat', 'active' => $activeRoute === 'dosen.praoutline.review-saya'], + ['title' => 'Pencarian Usulan', 'href' => route('dosen.praoutline.cari'), 'icon' => 'search', 'active' => $activeRoute === 'dosen.praoutline.cari'], + ['title' => 'Daftar Bimbingan Saya', 'href' => route('dosen.praoutline.bimbingan'), 'icon' => 'users', 'active' => $activeRoute === 'dosen.praoutline.bimbingan'], + ['title' => 'Statistik Usulan', 'href' => route('dosen.praoutline.statistik'), 'icon' => 'chart', 'active' => $activeRoute === 'dosen.praoutline.statistik'], + ['title' => 'Pemberitahuan', 'href' => route('dosen.praoutline.pemberitahuan'), 'icon' => 'bell', 'active' => $activeRoute === 'dosen.praoutline.pemberitahuan'], + ], + ], + [ + 'title' => 'Lainnya', + 'icon' => 'grid', + 'items' => [ + ['title' => 'Pengumuman', 'href' => route('dosen.pengumuman.index'), 'icon' => 'megaphone', 'active' => $activeRoute === 'dosen.pengumuman.index' || $activeRoute === 'dosen.pengumuman.show'], + ['title' => 'Akun Pengguna', 'href' => route('dosen.profile'), 'icon' => 'user', 'active' => $activeRoute === 'dosen.profile'], + ['title' => 'Dokumen Sidang', 'href' => 'https://edoxid.untan.ac.id/', 'icon' => 'document', 'external' => true], + ['title' => 'Early Warning', 'href' => route('dosen.early-warning'), 'icon' => 'warning', 'active' => $activeRoute === 'dosen.early-warning'], + ['title' => 'Konsultasi Skripsi', 'href' => 'https://spota.untan.ac.id/konsultasi/', 'icon' => 'chat', 'external' => true], + ['title' => 'Statistik Seminar', 'href' => 'https://spota.untan.ac.id/cek_banyak_sidang.php', 'icon' => 'chart', 'external' => true], + ['title' => 'Konsultasi KP', 'href' => 'https://informatika.untan.ac.id/konsultasi/', 'icon' => 'chat', 'external' => true], + ['title' => 'Pra LIRS (Dosen PA)', 'href' => route('dosen.pra-lirs'), 'icon' => 'clipboard', 'active' => $activeRoute === 'dosen.pra-lirs'], + ['title' => 'Evaluasi Mahasiswa', 'href' => 'https://spota.untan.ac.id/steven/rekapMahasiswaEvaluasi.php?angkatan='.(date('Y') - 5).'&show=belumlulus', 'icon' => 'chart', 'external' => true], + ], + ], + ], + ]; + } +} diff --git a/rebuild/artisan b/rebuild/artisan new file mode 100644 index 0000000..c35e31d --- /dev/null +++ b/rebuild/artisan @@ -0,0 +1,18 @@ +#!/usr/bin/env php +handleCommand(new ArgvInput); + +exit($status); diff --git a/rebuild/bootstrap/app.php b/rebuild/bootstrap/app.php new file mode 100644 index 0000000..a0e456d --- /dev/null +++ b/rebuild/bootstrap/app.php @@ -0,0 +1,21 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware): void { + $middleware->alias([ + 'legacy.role' => EnsureLegacyRole::class, + ]); + }) + ->withExceptions(function (Exceptions $exceptions): void { + // + })->create(); diff --git a/rebuild/bootstrap/providers.php b/rebuild/bootstrap/providers.php new file mode 100644 index 0000000..fc94ae6 --- /dev/null +++ b/rebuild/bootstrap/providers.php @@ -0,0 +1,7 @@ +=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "dflydev/dot-access-data", + "version": "v3.0.3", + "source": { + "type": "git", + "url": "https://github.com/dflydev/dflydev-dot-access-data.git", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dflydev/dflydev-dot-access-data/zipball/a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "reference": "a23a2bf4f31d3518f3ecb38660c95715dfead60f", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.42", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.3", + "scrutinizer/ocular": "1.6.0", + "squizlabs/php_codesniffer": "^3.5", + "vimeo/psalm": "^4.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Dflydev\\DotAccessData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dragonfly Development Inc.", + "email": "info@dflydev.com", + "homepage": "http://dflydev.com" + }, + { + "name": "Beau Simensen", + "email": "beau@dflydev.com", + "homepage": "http://beausimensen.com" + }, + { + "name": "Carlos Frutos", + "email": "carlos@kiwing.it", + "homepage": "https://github.com/cfrutos" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com" + } + ], + "description": "Given a deep data structure, access data by dot notation.", + "homepage": "https://github.com/dflydev/dflydev-dot-access-data", + "keywords": [ + "access", + "data", + "dot", + "notation" + ], + "support": { + "issues": "https://github.com/dflydev/dflydev-dot-access-data/issues", + "source": "https://github.com/dflydev/dflydev-dot-access-data/tree/v3.0.3" + }, + "time": "2024-07-08T12:26:09+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.1.0" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2025-08-10T19:31:58+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" + }, + { + "name": "dragonmantank/cron-expression", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/dragonmantank/cron-expression.git", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "shasum": "" + }, + "require": { + "php": "^8.2|^8.3|^8.4|^8.5" + }, + "replace": { + "mtdowling/cron-expression": "^1.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Cron\\": "src/Cron/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Tankersley", + "email": "chris@ctankersley.com", + "homepage": "https://github.com/dragonmantank" + } + ], + "description": "CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due", + "keywords": [ + "cron", + "schedule" + ], + "support": { + "issues": "https://github.com/dragonmantank/cron-expression/issues", + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://github.com/dragonmantank", + "type": "github" + } + ], + "time": "2025-10-31T18:51:33+00:00" + }, + { + "name": "egulias/email-validator", + "version": "4.0.4", + "source": { + "type": "git", + "url": "https://github.com/egulias/EmailValidator.git", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2.0 || ^3.0", + "php": ">=8.1", + "symfony/polyfill-intl-idn": "^1.26" + }, + "require-dev": { + "phpunit/phpunit": "^10.2", + "vimeo/psalm": "^5.12" + }, + "suggest": { + "ext-intl": "PHP Internationalization Libraries are required to use the SpoofChecking validation" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Egulias\\EmailValidator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Eduardo Gulias Davis" + } + ], + "description": "A library for validating emails against several RFCs", + "homepage": "https://github.com/egulias/EmailValidator", + "keywords": [ + "email", + "emailvalidation", + "emailvalidator", + "validation", + "validator" + ], + "support": { + "issues": "https://github.com/egulias/EmailValidator/issues", + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" + }, + "funding": [ + { + "url": "https://github.com/egulias", + "type": "github" + } + ], + "time": "2025-03-06T22:45:56+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" + }, + "require-dev": { + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^9", + "squizlabs/php_codesniffer": "^4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Fruitcake\\Cors\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fruitcake", + "homepage": "https://fruitcake.nl" + }, + { + "name": "Barryvdh", + "email": "barryvdh@gmail.com" + } + ], + "description": "Cross-origin resource sharing library for the Symfony HttpFoundation", + "homepage": "https://github.com/fruitcake/php-cors", + "keywords": [ + "cors", + "laravel", + "symfony" + ], + "support": { + "issues": "https://github.com/fruitcake/php-cors/issues", + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" + }, + "funding": [ + { + "url": "https://fruitcake.nl", + "type": "custom" + }, + { + "url": "https://github.com/barryvdh", + "type": "github" + } + ], + "time": "2025-12-03T09:33:47+00:00" + }, + { + "name": "graham-campbell/result-type", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.5" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:43:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "481557b130ef3790cf82b713667b43030dc9c957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.9.0", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.9.0" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2026-03-10T16:41:02+00:00" + }, + { + "name": "guzzlehttp/uri-template", + "version": "v1.0.5", + "source": { + "type": "git", + "url": "https://github.com/guzzle/uri-template.git", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-php80": "^1.24" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", + "uri-template/tests": "1.0.0" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\UriTemplate\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + } + ], + "description": "A polyfill class for uri_template of PHP", + "keywords": [ + "guzzlehttp", + "uri-template" + ], + "support": { + "issues": "https://github.com/guzzle/uri-template/issues", + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", + "type": "tidelift" + } + ], + "time": "2025-08-22T14:27:06+00:00" + }, + { + "name": "laravel/framework", + "version": "v13.7.0", + "source": { + "type": "git", + "url": "https://github.com/laravel/framework.git", + "reference": "f13b85b2cce7ef5e8f3bcdf2b6c6364bbdedae0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/framework/zipball/f13b85b2cce7ef5e8f3bcdf2b6c6364bbdedae0b", + "reference": "f13b85b2cce7ef5e8f3bcdf2b6c6364bbdedae0b", + "shasum": "" + }, + "require": { + "brick/math": "^0.14.2 || ^0.15 || ^0.16 || ^0.17", + "composer-runtime-api": "^2.2", + "doctrine/inflector": "^2.0.5", + "dragonmantank/cron-expression": "^3.4", + "egulias/email-validator": "^4.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-hash": "*", + "ext-mbstring": "*", + "ext-openssl": "*", + "ext-session": "*", + "ext-tokenizer": "*", + "fruitcake/php-cors": "^1.3", + "guzzlehttp/guzzle": "^7.8.2", + "guzzlehttp/promises": "^2.0.3", + "guzzlehttp/uri-template": "^1.0", + "laravel/prompts": "^0.3.0", + "laravel/serializable-closure": "^2.0.10", + "league/commonmark": "^2.8.1", + "league/flysystem": "^3.25.1", + "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", + "monolog/monolog": "^3.0", + "nesbot/carbon": "^3.8.4", + "nunomaduro/termwind": "^2.0", + "php": "^8.3", + "psr/container": "^1.1.1 || ^2.0.1", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "psr/simple-cache": "^1.0 || ^2.0 || ^3.0", + "ramsey/uuid": "^4.7", + "symfony/console": "^7.4.0 || ^8.0.0", + "symfony/error-handler": "^7.4.0 || ^8.0.0", + "symfony/finder": "^7.4.0 || ^8.0.0", + "symfony/http-foundation": "^7.4.0 || ^8.0.0", + "symfony/http-kernel": "^7.4.0 || ^8.0.0", + "symfony/mailer": "^7.4.0 || ^8.0.0", + "symfony/mime": "^7.4.0 || ^8.0.0", + "symfony/polyfill-php84": "^1.33", + "symfony/polyfill-php85": "^1.33", + "symfony/polyfill-php86": "^1.36", + "symfony/process": "^7.4.5 || ^8.0.5", + "symfony/routing": "^7.4.0 || ^8.0.0", + "symfony/uid": "^7.4.0 || ^8.0.0", + "symfony/var-dumper": "^7.4.0 || ^8.0.0", + "tijsverkoyen/css-to-inline-styles": "^2.2.5", + "vlucas/phpdotenv": "^5.6.1", + "voku/portable-ascii": "^2.0.2" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "provide": { + "psr/container-implementation": "1.1 || 2.0", + "psr/log-implementation": "1.0 || 2.0 || 3.0", + "psr/simple-cache-implementation": "1.0 || 2.0 || 3.0" + }, + "replace": { + "illuminate/auth": "self.version", + "illuminate/broadcasting": "self.version", + "illuminate/bus": "self.version", + "illuminate/cache": "self.version", + "illuminate/collections": "self.version", + "illuminate/concurrency": "self.version", + "illuminate/conditionable": "self.version", + "illuminate/config": "self.version", + "illuminate/console": "self.version", + "illuminate/container": "self.version", + "illuminate/contracts": "self.version", + "illuminate/cookie": "self.version", + "illuminate/database": "self.version", + "illuminate/encryption": "self.version", + "illuminate/events": "self.version", + "illuminate/filesystem": "self.version", + "illuminate/hashing": "self.version", + "illuminate/http": "self.version", + "illuminate/json-schema": "self.version", + "illuminate/log": "self.version", + "illuminate/macroable": "self.version", + "illuminate/mail": "self.version", + "illuminate/notifications": "self.version", + "illuminate/pagination": "self.version", + "illuminate/pipeline": "self.version", + "illuminate/process": "self.version", + "illuminate/queue": "self.version", + "illuminate/redis": "self.version", + "illuminate/reflection": "self.version", + "illuminate/routing": "self.version", + "illuminate/session": "self.version", + "illuminate/support": "self.version", + "illuminate/testing": "self.version", + "illuminate/translation": "self.version", + "illuminate/validation": "self.version", + "illuminate/view": "self.version", + "spatie/once": "*" + }, + "require-dev": { + "ably/ably-php": "^1.0", + "aws/aws-sdk-php": "^3.322.9", + "ext-gmp": "*", + "fakerphp/faker": "^1.24", + "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", + "league/flysystem-aws-s3-v3": "^3.25.1", + "league/flysystem-ftp": "^3.25.1", + "league/flysystem-path-prefixing": "^3.25.1", + "league/flysystem-read-only": "^3.25.1", + "league/flysystem-sftp-v3": "^3.25.1", + "mockery/mockery": "^1.6.10", + "opis/json-schema": "^2.4.1", + "orchestra/testbench-core": "^11.0.0", + "pda/pheanstalk": "^7.0.0 || ^8.0.0", + "php-http/discovery": "^1.15", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^11.5.50 || ^12.5.8 || ^13.0.3", + "predis/predis": "^2.3 || ^3.0", + "rector/rector": "^2.3", + "resend/resend-php": "^1.0", + "symfony/cache": "^7.4.0 || ^8.0.0", + "symfony/http-client": "^7.4.0 || ^8.0.0", + "symfony/psr-http-message-bridge": "^7.4.0 || ^8.0.0", + "symfony/translation": "^7.4.0 || ^8.0.0" + }, + "suggest": { + "ably/ably-php": "Required to use the Ably broadcast driver (^1.0).", + "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.322.9).", + "brianium/paratest": "Required to run tests in parallel (^7.0 || ^8.0).", + "ext-apcu": "Required to use the APC cache driver.", + "ext-fileinfo": "Required to use the Filesystem class.", + "ext-ftp": "Required to use the Flysystem FTP driver.", + "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().", + "ext-memcached": "Required to use the memcache cache driver.", + "ext-pcntl": "Required to use all features of the queue worker and console signal trapping.", + "ext-pdo": "Required to use all database features.", + "ext-posix": "Required to use all features of the queue worker.", + "ext-redis": "Required to use the Redis cache and queue drivers (^4.0 || ^5.0 || ^6.0).", + "fakerphp/faker": "Required to generate fake data using the fake() helper (^1.23).", + "filp/whoops": "Required for friendly error pages in development (^2.14.3).", + "laravel/tinker": "Required to use the tinker console command (^2.0).", + "league/flysystem-aws-s3-v3": "Required to use the Flysystem S3 driver (^3.25.1).", + "league/flysystem-ftp": "Required to use the Flysystem FTP driver (^3.25.1).", + "league/flysystem-path-prefixing": "Required to use the scoped driver (^3.25.1).", + "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", + "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", + "mockery/mockery": "Required to use mocking (^1.6).", + "pda/pheanstalk": "Required to use the beanstalk queue driver (^7.0 || ^8.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", + "phpunit/phpunit": "Required to use assertions and run tests (^11.5.50 || ^12.5.8 || ^13.0.3).", + "predis/predis": "Required to use the predis connector (^2.3 || ^3.0).", + "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", + "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0 || ^7.0).", + "resend/resend-php": "Required to enable support for the Resend mail transport (^0.10.0 || ^1.0).", + "spatie/fork": "Required to use the 'fork' concurrency driver (^1.2).", + "symfony/cache": "Required to PSR-6 cache bridge (^7.4 || ^8.0).", + "symfony/filesystem": "Required to enable support for relative symbolic links (^7.4 || ^8.0).", + "symfony/http-client": "Required to enable support for the Symfony API mail transports (^7.4 || ^8.0).", + "symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^7.4 || ^8.0).", + "symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^7.4 || ^8.0).", + "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^7.4 || ^8.0)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "13.0.x-dev" + } + }, + "autoload": { + "files": [ + "src/Illuminate/Collections/functions.php", + "src/Illuminate/Collections/helpers.php", + "src/Illuminate/Events/functions.php", + "src/Illuminate/Filesystem/functions.php", + "src/Illuminate/Foundation/helpers.php", + "src/Illuminate/Log/functions.php", + "src/Illuminate/Reflection/helpers.php", + "src/Illuminate/Support/functions.php", + "src/Illuminate/Support/helpers.php" + ], + "psr-4": { + "Illuminate\\": "src/Illuminate/", + "Illuminate\\Support\\": [ + "src/Illuminate/Macroable/", + "src/Illuminate/Collections/", + "src/Illuminate/Conditionable/", + "src/Illuminate/Reflection/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Laravel Framework.", + "homepage": "https://laravel.com", + "keywords": [ + "framework", + "laravel" + ], + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2026-04-28T17:18:25+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.3.17", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "6a82ac19a28b916ae0885828795dbd4c59d9a818" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/6a82ac19a28b916ae0885828795dbd4c59d9a818", + "reference": "6a82ac19a28b916ae0885828795dbd4c59d9a818", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.2", + "ext-mbstring": "*", + "php": "^8.1", + "symfony/console": "^6.2|^7.0|^8.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "illuminate/collections": "^10.0|^11.0|^12.0|^13.0", + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3|^3.4|^4.0", + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.3.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Add beautiful and user-friendly forms to your command-line applications.", + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.3.17" + }, + "time": "2026-04-20T16:07:33+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.13", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b566ee0dd251f3c4078bed003a7ce015f5ea6dce", + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", + "nesbot/carbon": "^2.67|^3.0", + "pestphp/pest": "^2.36|^3.0|^4.0", + "phpstan/phpstan": "^2.0", + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2026-04-16T14:03:50+00:00" + }, + { + "name": "laravel/tinker", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/tinker.git", + "reference": "4faba77764bd33411735936acdf30446d058c78b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/tinker/zipball/4faba77764bd33411735936acdf30446d058c78b", + "reference": "4faba77764bd33411735936acdf30446d058c78b", + "shasum": "" + }, + "require": { + "illuminate/console": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "php": "^8.1", + "psy/psysh": "^0.12.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0|^8.0" + }, + "require-dev": { + "mockery/mockery": "~1.3.3|^1.4.2", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5|^11.5" + }, + "suggest": { + "illuminate/database": "The Illuminate Database package (^8.0|^9.0|^10.0|^11.0|^12.0|^13.0)." + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Tinker\\TinkerServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Tinker\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Powerful REPL for the Laravel framework.", + "keywords": [ + "REPL", + "Tinker", + "laravel", + "psysh" + ], + "support": { + "issues": "https://github.com/laravel/tinker/issues", + "source": "https://github.com/laravel/tinker/tree/v3.0.2" + }, + "time": "2026-03-17T14:54:13+00:00" + }, + { + "name": "league/commonmark", + "version": "2.8.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/commonmark.git", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "league/config": "^1.1.1", + "php": "^7.4 || ^8.0", + "psr/event-dispatcher": "^1.0", + "symfony/deprecation-contracts": "^2.1 || ^3.0", + "symfony/polyfill-php80": "^1.16" + }, + "require-dev": { + "cebe/markdown": "^1.0", + "commonmark/cmark": "0.31.1", + "commonmark/commonmark.js": "0.31.1", + "composer/package-versions-deprecated": "^1.8", + "embed/embed": "^4.4", + "erusev/parsedown": "^1.0", + "ext-json": "*", + "github/gfm": "0.29.0", + "michelf/php-markdown": "^1.4 || ^2.0", + "nyholm/psr7": "^1.5", + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", + "scrutinizer/ocular": "^1.8.1", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", + "unleashedtech/php-coding-standard": "^3.1.1", + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" + }, + "suggest": { + "symfony/yaml": "v2.3+ required if using the Front Matter extension" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.9-dev" + } + }, + "autoload": { + "psr-4": { + "League\\CommonMark\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Highly-extensible PHP Markdown parser which fully supports the CommonMark spec and GitHub-Flavored Markdown (GFM)", + "homepage": "https://commonmark.thephpleague.com", + "keywords": [ + "commonmark", + "flavored", + "gfm", + "github", + "github-flavored", + "markdown", + "md", + "parser" + ], + "support": { + "docs": "https://commonmark.thephpleague.com/", + "forum": "https://github.com/thephpleague/commonmark/discussions", + "issues": "https://github.com/thephpleague/commonmark/issues", + "rss": "https://github.com/thephpleague/commonmark/releases.atom", + "source": "https://github.com/thephpleague/commonmark" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/commonmark", + "type": "tidelift" + } + ], + "time": "2026-03-19T13:16:38+00:00" + }, + { + "name": "league/config", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/config.git", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/config/zipball/754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "reference": "754b3604fb2984c71f4af4a9cbe7b57f346ec1f3", + "shasum": "" + }, + "require": { + "dflydev/dot-access-data": "^3.0.1", + "nette/schema": "^1.2", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.8.2", + "phpunit/phpunit": "^9.5.5", + "scrutinizer/ocular": "^1.8.1", + "unleashedtech/php-coding-standard": "^3.1", + "vimeo/psalm": "^4.7.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Config\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" + } + ], + "description": "Define configuration arrays with strict schemas and access values with dot notation", + "homepage": "https://config.thephpleague.com", + "keywords": [ + "array", + "config", + "configuration", + "dot", + "dot-access", + "nested", + "schema" + ], + "support": { + "docs": "https://config.thephpleague.com/", + "issues": "https://github.com/thephpleague/config/issues", + "rss": "https://github.com/thephpleague/config/releases.atom", + "source": "https://github.com/thephpleague/config" + }, + "funding": [ + { + "url": "https://www.colinodell.com/sponsor", + "type": "custom" + }, + { + "url": "https://www.paypal.me/colinpodell/10.00", + "type": "custom" + }, + { + "url": "https://github.com/colinodell", + "type": "github" + } + ], + "time": "2022-12-11T20:36:23+00:00" + }, + { + "name": "league/flysystem", + "version": "3.33.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem.git", + "reference": "570b8871e0ce693764434b29154c54b434905350" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/570b8871e0ce693764434b29154c54b434905350", + "reference": "570b8871e0ce693764434b29154c54b434905350", + "shasum": "" + }, + "require": { + "league/flysystem-local": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "async-aws/core": "<1.19.0", + "async-aws/s3": "<1.14.0", + "aws/aws-sdk-php": "3.209.31 || 3.210.0", + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1", + "phpseclib/phpseclib": "3.0.15", + "symfony/http-client": "<5.2" + }, + "require-dev": { + "async-aws/s3": "^1.5 || ^2.0", + "async-aws/simple-s3": "^1.1 || ^2.0", + "aws/aws-sdk-php": "^3.295.10", + "composer/semver": "^3.0", + "ext-fileinfo": "*", + "ext-ftp": "*", + "ext-mongodb": "^1.3|^2", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.5", + "google/cloud-storage": "^1.23", + "guzzlehttp/psr7": "^2.6", + "microsoft/azure-storage-blob": "^1.1", + "mongodb/mongodb": "^1.2|^2", + "phpseclib/phpseclib": "^3.0.36", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.11|^10.0", + "sabre/dav": "^4.6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "File storage abstraction for PHP", + "keywords": [ + "WebDAV", + "aws", + "cloud", + "file", + "files", + "filesystem", + "filesystems", + "ftp", + "s3", + "sftp", + "storage" + ], + "support": { + "issues": "https://github.com/thephpleague/flysystem/issues", + "source": "https://github.com/thephpleague/flysystem/tree/3.33.0" + }, + "time": "2026-03-25T07:59:30+00:00" + }, + { + "name": "league/flysystem-local", + "version": "3.31.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-local.git", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "league/flysystem": "^3.0.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\Local\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Local filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "file", + "files", + "filesystem", + "local" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" + }, + "time": "2026-01-23T15:30:45+00:00" + }, + { + "name": "league/mime-type-detection", + "version": "1.16.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/mime-type-detection.git", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", + "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "shasum": "" + }, + "require": { + "ext-fileinfo": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^0.12.68", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\MimeTypeDetection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "Mime-type detection for Flysystem", + "support": { + "issues": "https://github.com/thephpleague/mime-type-detection/issues", + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + }, + "funding": [ + { + "url": "https://github.com/frankdejonge", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/league/flysystem", + "type": "tidelift" + } + ], + "time": "2024-09-21T08:32:55+00:00" + }, + { + "name": "league/uri", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri.git", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", + "shasum": "" + }, + "require": { + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" + }, + "conflict": { + "league/uri-schemes": "^1.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", + "ext-fileinfo": "to create Data URI from file contennts", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "URI manipulation library", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "URN", + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "middleware", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc2141", + "rfc3986", + "rfc3987", + "rfc6570", + "rfc8141", + "uri", + "uri-template", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-15T20:22:25+00:00" + }, + { + "name": "league/uri-interfaces", + "version": "7.8.1", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/uri-interfaces.git", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^8.1", + "psr/http-message": "^1.1 || ^2.0" + }, + "suggest": { + "ext-bcmath": "to improve IPV4 host parsing", + "ext-gmp": "to improve IPV4 host parsing", + "ext-intl": "to handle IDN host with the best performance", + "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.x-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Uri\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ignace Nyamagana Butera", + "email": "nyamsprod@gmail.com", + "homepage": "https://nyamsprod.com" + } + ], + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", + "homepage": "https://uri.thephpleague.com", + "keywords": [ + "data-uri", + "file-uri", + "ftp", + "hostname", + "http", + "https", + "parse_str", + "parse_url", + "psr-7", + "query-string", + "querystring", + "rfc3986", + "rfc3987", + "rfc6570", + "uri", + "url", + "ws" + ], + "support": { + "docs": "https://uri.thephpleague.com", + "forum": "https://thephpleague.slack.com", + "issues": "https://github.com/thephpleague/uri-src/issues", + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" + }, + "funding": [ + { + "url": "https://github.com/sponsors/nyamsprod", + "type": "github" + } + ], + "time": "2026-03-08T20:05:35+00:00" + }, + { + "name": "monolog/monolog", + "version": "3.10.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" + }, + "provide": { + "psr/log-implementation": "3.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^3.0", + "doctrine/couchdb": "~1.0@dev", + "elasticsearch/elasticsearch": "^7 || ^8", + "ext-json": "*", + "graylog2/gelf-php": "^1.4.2 || ^2.0", + "guzzlehttp/guzzle": "^7.4.5", + "guzzlehttp/psr7": "^2.2", + "mongodb/mongodb": "^1.8 || ^2.0", + "php-amqplib/php-amqplib": "~2.4 || ^3", + "php-console/php-console": "^3.1.8", + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^10.5.17 || ^11.0.7", + "predis/predis": "^1.1 || ^2", + "rollbar/rollbar": "^4.0", + "ruflin/elastica": "^7 || ^8", + "symfony/mailer": "^5.4 || ^6", + "symfony/mime": "^5.4 || ^6" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-curl": "Required to send log messages using the IFTTTHandler, the LogglyHandler, the SendGridHandler, the SlackWebhookHandler or the TelegramBotHandler", + "ext-mbstring": "Allow to work properly with unicode symbols", + "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)", + "ext-openssl": "Required to send log messages using SSL", + "ext-sockets": "Allow sending log messages to a Syslog server (via UDP driver)", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "https://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "support": { + "issues": "https://github.com/Seldaek/monolog/issues", + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", + "type": "tidelift" + } + ], + "time": "2026-01-02T08:56:05+00:00" + }, + { + "name": "nesbot/carbon", + "version": "3.11.4", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/e890471a3494740f7d9326d72ce6a8c559ffee60", + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "<100.0", + "ext-json": "*", + "php": "^8.1", + "psr/clock": "^1.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^3.6.3 || ^4.0", + "doctrine/orm": "^2.15.2 || ^3.0", + "friendsofphp/php-cs-fixer": "^v3.87.1", + "kylekatarnls/multi-tester": "^2.5.3", + "phpmd/phpmd": "^2.15.0", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbonphp.github.io/carbon/", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2026-04-07T09:57:54+00:00" + }, + { + "name": "nette/schema", + "version": "v1.3.5", + "source": { + "type": "git", + "url": "https://github.com/nette/schema.git", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", + "shasum": "" + }, + "require": { + "nette/utils": "^4.0", + "php": "8.1 - 8.5" + }, + "require-dev": { + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.6", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", + "tracy/tracy": "^2.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "📐 Nette Schema: validating data structures against a given Schema.", + "homepage": "https://nette.org", + "keywords": [ + "config", + "nette" + ], + "support": { + "issues": "https://github.com/nette/schema/issues", + "source": "https://github.com/nette/schema/tree/v1.3.5" + }, + "time": "2026-02-23T03:47:12+00:00" + }, + { + "name": "nette/utils", + "version": "v4.1.3", + "source": { + "type": "git", + "url": "https://github.com/nette/utils.git", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe", + "shasum": "" + }, + "require": { + "php": "8.2 - 8.5" + }, + "conflict": { + "nette/finder": "<3", + "nette/schema": "<1.2.2" + }, + "require-dev": { + "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.5", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", + "tracy/tracy": "^2.9" + }, + "suggest": { + "ext-gd": "to use Image", + "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()", + "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()", + "ext-json": "to use Nette\\Utils\\Json", + "ext-mbstring": "to use Strings::lower() etc...", + "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Nette\\": "src" + }, + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0-only", + "GPL-3.0-only" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "https://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "https://nette.org/contributors" + } + ], + "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.", + "homepage": "https://nette.org", + "keywords": [ + "array", + "core", + "datetime", + "images", + "json", + "nette", + "paginator", + "password", + "slugify", + "string", + "unicode", + "utf-8", + "utility", + "validation" + ], + "support": { + "issues": "https://github.com/nette/utils/issues", + "source": "https://github.com/nette/utils/tree/v4.1.3" + }, + "time": "2026-02-13T03:05:33+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.4.4 || ^8.0.4" + }, + "require-dev": { + "illuminate/console": "^11.47.0", + "laravel/pint": "^1.27.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5 || ^8.0.4", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "It's like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2026-02-16T23:10:27+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.5", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:41:33+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "psy/psysh", + "version": "v0.12.22", + "source": { + "type": "git", + "url": "https://github.com/bobthecow/psysh.git", + "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/3be75d5b9244936dd4ac62ade2bfb004d13acf0f", + "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-tokenizer": "*", + "nikic/php-parser": "^5.0 || ^4.0", + "php": "^8.0 || ^7.4", + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + }, + "conflict": { + "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" + }, + "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", + "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", + "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." + }, + "bin": [ + "bin/psysh" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "0.12.x-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Psy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Justin Hileman", + "email": "justin@justinhileman.info" + } + ], + "description": "An interactive shell for modern PHP.", + "homepage": "https://psysh.org", + "keywords": [ + "REPL", + "console", + "interactive", + "shell" + ], + "support": { + "issues": "https://github.com/bobthecow/psysh/issues", + "source": "https://github.com/bobthecow/psysh/tree/v0.12.22" + }, + "time": "2026-03-22T23:03:24+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "ramsey/collection", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/ramsey/collection.git", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "captainhook/plugin-composer": "^5.3", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", + "hamcrest/hamcrest-php": "^2.0", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + }, + "ramsey/conventional-commits": { + "configFile": "conventional-commits.json" + } + }, + "autoload": { + "psr-4": { + "Ramsey\\Collection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ben Ramsey", + "email": "ben@benramsey.com", + "homepage": "https://benramsey.com" + } + ], + "description": "A PHP library for representing and manipulating collections.", + "keywords": [ + "array", + "collection", + "hash", + "map", + "queue", + "set" + ], + "support": { + "issues": "https://github.com/ramsey/collection/issues", + "source": "https://github.com/ramsey/collection/tree/2.1.1" + }, + "time": "2025-03-22T05:38:12+00:00" + }, + { + "name": "ramsey/uuid", + "version": "4.9.2", + "source": { + "type": "git", + "url": "https://github.com/ramsey/uuid.git", + "reference": "8429c78ca35a09f27565311b98101e2826affde0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", + "reference": "8429c78ca35a09f27565311b98101e2826affde0", + "shasum": "" + }, + "require": { + "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", + "php": "^8.0", + "ramsey/collection": "^1.2 || ^2.0" + }, + "replace": { + "rhumsaa/uuid": "self.version" + }, + "require-dev": { + "captainhook/captainhook": "^5.25", + "captainhook/plugin-composer": "^5.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", + "paragonie/random-lib": "^2", + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" + }, + "suggest": { + "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", + "ext-gmp": "Enables faster math with arbitrary-precision integers using GMP.", + "ext-uuid": "Enables the use of PeclUuidTimeGenerator and PeclUuidRandomGenerator.", + "paragonie/random-lib": "Provides RandomLib for use with the RandomLibAdapter", + "ramsey/uuid-doctrine": "Allows the use of Ramsey\\Uuid\\Uuid as Doctrine field type." + }, + "type": "library", + "extra": { + "captainhook": { + "force-install": true + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Ramsey\\Uuid\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A PHP library for generating and working with universally unique identifiers (UUIDs).", + "keywords": [ + "guid", + "identifier", + "uuid" + ], + "support": { + "issues": "https://github.com/ramsey/uuid/issues", + "source": "https://github.com/ramsey/uuid/tree/4.9.2" + }, + "time": "2025-12-14T04:43:48+00:00" + }, + { + "name": "symfony/clock", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/clock.git", + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3", + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "psr/clock": "^1.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/now.php" + ], + "psr-4": { + "Symfony\\Component\\Clock\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Decouples applications from the system clock", + "homepage": "https://symfony.com", + "keywords": [ + "clock", + "psr20", + "time" + ], + "support": { + "source": "https://github.com/symfony/clock/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/console", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/7113778e2e91f4709cb3194a75dfa9c0d028d94d", + "reference": "7113778e2e91f4709cb3194a75dfa9c0d028d94d", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.4|^8.0" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/lock": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-29T15:02:55+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "3665cfade90565430909b906394c73c8739e57d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/3665cfade90565430909b906394c73c8739e57d0", + "reference": "3665cfade90565430909b906394c73c8739e57d0", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-18T13:51:42+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/error-handler", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/error-handler.git", + "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c1119fe8dcfc3825ec74ec061b96ef0c8f281517", + "reference": "c1119fe8dcfc3825ec74ec061b96ef0c8f281517", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "psr/log": "^1|^2|^3", + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^7.4|^8.0" + }, + "conflict": { + "symfony/deprecation-contracts": "<2.5" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" + }, + "bin": [ + "Resources/bin/patch-type-declarations" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\ErrorHandler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to manage errors and ease debugging PHP code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/error-handler/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/security-http": "<7.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-18T13:51:42+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/finder", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "8da41214757b87d97f181e3d14a4179286151007" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/8da41214757b87d97f181e3d14a4179286151007", + "reference": "8da41214757b87d97f181e3d14a4179286151007", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "symfony/filesystem": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/http-foundation", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-foundation.git", + "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02656f7ebeae5c155d659e946f6b3a33df24051b", + "reference": "02656f7ebeae5c155d659e946f6b3a33df24051b", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.1" + }, + "conflict": { + "doctrine/dbal": "<4.3" + }, + "require-dev": { + "doctrine/dbal": "^4.3", + "predis/predis": "^1.1|^2.0", + "symfony/cache": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/rate-limiter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpFoundation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Defines an object-oriented layer for the HTTP specification", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-foundation/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/http-kernel", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-kernel.git", + "reference": "1770f6818d83b2fddc12185025b93f39a90cb628" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1770f6818d83b2fddc12185025b93f39a90cb628", + "reference": "1770f6818d83b2fddc12185025b93f39a90cb628", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "psr/log": "^1|^2|^3", + "symfony/error-handler": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/flex": "<2.10", + "symfony/http-client-contracts": "<2.5", + "symfony/translation-contracts": "<2.5", + "twig/twig": "<3.21" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/cache": "^1.0|^2.0|^3.0", + "symfony/browser-kit": "^7.4|^8.0", + "symfony/clock": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/css-selector": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/dom-crawler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-client-contracts": "^2.5|^3", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/routing": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/stopwatch": "^7.4|^8.0", + "symfony/translation": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3", + "symfony/uid": "^7.4|^8.0", + "symfony/validator": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0", + "symfony/var-exporter": "^7.4|^8.0", + "twig/twig": "^3.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpKernel\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a structured process for converting a Request into a Response", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-kernel/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-31T21:14:05+00:00" + }, + { + "name": "symfony/mailer", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/ca5f6edaf8780ece814404b58a4482b22b509c56", + "reference": "ca5f6edaf8780ece814404b58a4482b22b509c56", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=8.4", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/mime": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/http-client-contracts": "<2.5" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", + "symfony/twig-bridge": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/mime", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "a9fcb293650c054b62a5b406f4e92e7b711ea333" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/a9fcb293650c054b62a5b406f4e92e7b711ea333", + "reference": "a9fcb293650c054b62a5b406f4e92e7b711ea333", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "league/html-to-markdown": "^5.0", + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/property-access": "^7.4|^8.0", + "symfony/property-info": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-29T15:02:55+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-26T13:13:48+00:00" + }, + { + "name": "symfony/polyfill-intl-idn", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-idn.git", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "shasum": "" + }, + "require": { + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-10T14:38:51+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T17:25:58+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-php84", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/88486db2c389b290bf87ff1de7ebc1e13e42bb06", + "reference": "88486db2c389b290bf87ff1de7ebc1e13e42bb06", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php84/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T18:47:49+00:00" + }, + { + "name": "symfony/polyfill-php85", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/fcfa4973a9917cef23f2e38774da74a2b7d115ee", + "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-26T13:10:57+00:00" + }, + { + "name": "symfony/polyfill-php86", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php86.git", + "reference": "33d8fc5a705481e21fe3a81212b26f9b1f61749c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/33d8fc5a705481e21fe3a81212b26f9b1f61749c", + "reference": "33d8fc5a705481e21fe3a81212b26f9b1f61749c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php86\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php86/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-26T13:13:48+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-uuid": "*" + }, + "suggest": { + "ext-uuid": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Uuid\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for uuid functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/process", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "reference": "cb8939aff03470d1a9d1d1b66d08c6fa71b3bbdc", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/routing", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/routing.git", + "reference": "75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/routing/zipball/75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038", + "reference": "75d1bd8e5da3424e4db2fc3ff0222cb4d0c73038", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Routing\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Maps an HTTP request to a set of configuration variables", + "homepage": "https://symfony.com", + "keywords": [ + "router", + "routing", + "uri", + "url" + ], + "support": { + "source": "https://github.com/symfony/routing/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-29T15:02:55+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T11:30:57+00:00" + }, + { + "name": "symfony/string", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/translation", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f", + "reference": "27c03ae3940de24ba2f71cfdbac824f2aa1fdf2f", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" + }, + "conflict": { + "nikic/php-parser": "<5.0", + "symfony/http-client-contracts": "<2.5", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-30T15:14:47+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.6.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "reference": "65a8bc82080447fae78373aa10f8d13b38338977", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-07-15T13:41:35+00:00" + }, + { + "name": "symfony/uid", + "version": "v8.0.9", + "source": { + "type": "git", + "url": "https://github.com/symfony/uid.git", + "reference": "4d9d6510bbe88ebb4608b7200d18606cdf80825c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/uid/zipball/4d9d6510bbe88ebb4608b7200d18606cdf80825c", + "reference": "4d9d6510bbe88ebb4608b7200d18606cdf80825c", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-uuid": "^1.15" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Uid\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Pineau", + "email": "lyrixx@lyrixx.info" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to generate and represent UIDs", + "homepage": "https://symfony.com", + "keywords": [ + "UID", + "ulid", + "uuid" + ], + "support": { + "source": "https://github.com/symfony/uid/tree/v8.0.9" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-30T16:10:06+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", + "reference": "cfb7badd53bf4177f6e9416cfbbccc13c0e773a1", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0" + }, + "conflict": { + "symfony/console": "<7.4", + "symfony/error-handler": "<7.4" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/process": "^7.4|^8.0", + "symfony/uid": "^7.4|^8.0", + "twig/twig": "^3.12" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v8.0.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-31T07:15:36+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "TijsVerkoyen\\CssToInlineStyles\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Tijs Verkoyen", + "email": "css_to_inline_styles@verkoyen.eu", + "role": "Developer" + } + ], + "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.", + "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", + "support": { + "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" + }, + "time": "2025-12-02T11:56:42+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.6.3", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "955e7815d677a3eaa7075231212f2110983adecc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.1.4", + "php": "^7.2.5 || ^8.0", + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-filter": "*", + "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + }, + "branch-alias": { + "dev-master": "5.6-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2025-12-27T19:49:13+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.1.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2026-04-26T05:33:54+00:00" + } + ], + "packages-dev": [ + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.4", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+00:00" + }, + { + "name": "laravel/agent-detector", + "version": "v2.0.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/agent-detector.git", + "reference": "90694b9256099591cf9e55d08c18ba7a00bf099f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/agent-detector/zipball/90694b9256099591cf9e55d08c18ba7a00bf099f", + "reference": "90694b9256099591cf9e55d08c18ba7a00bf099f", + "shasum": "" + }, + "require": { + "php": "^8.2.0" + }, + "require-dev": { + "laravel/pint": "^1.24.0", + "pestphp/pest": "^3.8.5|^4.1.0", + "pestphp/pest-plugin-type-coverage": "^3.0|^4.0.2", + "phpstan/phpstan": "^2.1.26", + "rector/rector": "^2.1.7", + "symfony/var-dumper": "^7.3.3" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Laravel\\AgentDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Detect if code is running in an AI agent or automated development environment", + "homepage": "https://github.com/laravel/agent-detector", + "keywords": [ + "Agent", + "ai", + "automation", + "claude", + "cursor", + "detection", + "devin", + "php" + ], + "support": { + "issues": "https://github.com/laravel/agent-detector/issues", + "source": "https://github.com/laravel/agent-detector" + }, + "time": "2026-04-29T18:32:34+00:00" + }, + { + "name": "laravel/pail", + "version": "v1.2.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/pail.git", + "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pail/zipball/aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf", + "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/console": "^10.24|^11.0|^12.0|^13.0", + "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0", + "illuminate/log": "^10.24|^11.0|^12.0|^13.0", + "illuminate/process": "^10.24|^11.0|^12.0|^13.0", + "illuminate/support": "^10.24|^11.0|^12.0|^13.0", + "nunomaduro/termwind": "^1.15|^2.0", + "php": "^8.2", + "symfony/console": "^6.0|^7.0|^8.0" + }, + "require-dev": { + "laravel/framework": "^10.24|^11.0|^12.0|^13.0", + "laravel/pint": "^1.13", + "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0", + "pestphp/pest": "^2.20|^3.0|^4.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", + "phpstan/phpstan": "^1.12.27", + "symfony/var-dumper": "^6.3|^7.0|^8.0", + "symfony/yaml": "^6.3|^7.0|^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Laravel\\Pail\\PailServiceProvider" + ] + }, + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\Pail\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Easily delve into your Laravel application's log files directly from the command line.", + "homepage": "https://github.com/laravel/pail", + "keywords": [ + "dev", + "laravel", + "logs", + "php", + "tail" + ], + "support": { + "issues": "https://github.com/laravel/pail/issues", + "source": "https://github.com/laravel/pail" + }, + "time": "2026-02-09T13:44:54+00:00" + }, + { + "name": "laravel/pao", + "version": "v1.0.6", + "source": { + "type": "git", + "url": "https://github.com/laravel/pao.git", + "reference": "02f62a64c2b60af44a418ee490fee193590d8269" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pao/zipball/02f62a64c2b60af44a418ee490fee193590d8269", + "reference": "02f62a64c2b60af44a418ee490fee193590d8269", + "shasum": "" + }, + "require": { + "laravel/agent-detector": "^2.0.0", + "php": "^8.3" + }, + "conflict": { + "laravel/framework": "<12.0.0", + "nunomaduro/collision": "<8.9.3", + "pestphp/pest": "<4.6.3 || >=6.0.0", + "phpunit/phpunit": "<12.5.23 || >=13.0.0 <13.1.7 || >=14.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.20.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench": "^10.11.0 || ^11.1.0", + "pestphp/pest": "^4.6.3 || ^5.0.0", + "pestphp/pest-plugin-type-coverage": "^4.0.4 || ^5.0.0", + "phpstan/phpstan": "^2.1.51", + "rector/rector": "^2.4.2", + "symfony/process": "^7.4.8 || ^8.1.0", + "symfony/var-dumper": "^7.4.8 || ^8.0.8" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Laravel\\Pao\\Drivers\\Pest\\Plugin" + ] + }, + "laravel": { + "providers": [ + "Laravel\\Pao\\Laravel\\ServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Laravel\\Pao\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Agent-optimized output for PHP testing tools", + "keywords": [ + "Agent", + "PHPStan", + "ai", + "dev", + "paratest", + "pest", + "php", + "phpunit", + "testing" + ], + "support": { + "issues": "https://github.com/laravel/pao/issues", + "source": "https://github.com/laravel/pao" + }, + "time": "2026-04-27T22:37:26+00:00" + }, + { + "name": "laravel/pint", + "version": "v1.29.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/0770e9b7fafd50d4586881d456d6eb41c9247a80", + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.2.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.95.1", + "illuminate/view": "^12.56.0", + "larastan/larastan": "^3.9.6", + "laravel-zero/framework": "^12.1.0", + "mockery/mockery": "^1.6.12", + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^3.8.6", + "shipfastlabs/agent-detector": "^1.1.3" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "dev", + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2026-04-20T15:26:14+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.9.4", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", + "php": "^8.2.0", + "symfony/console": "^7.4.8 || ^8.0.8" + }, + "conflict": { + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.9.6", + "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1", + "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2026-04-21T14:04:20+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "12.5.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "876099a072646c7745f673d7aeab5382c4439691" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/876099a072646c7745f673d7aeab5382c4439691", + "reference": "876099a072646c7745f673d7aeab5382c4439691", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.7.0", + "php": ">=8.3", + "phpunit/php-text-template": "^5.0", + "sebastian/complexity": "^5.0", + "sebastian/environment": "^8.0.3", + "sebastian/lines-of-code": "^4.0", + "sebastian/version": "^6.0", + "theseer/tokenizer": "^2.0.1" + }, + "require-dev": { + "phpunit/phpunit": "^12.5.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.5.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/12.5.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2026-04-15T08:23:17+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "reference": "3d1cd096ef6bea4bf2762ba586e35dbd317cbfd5", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" + } + ], + "time": "2026-02-02T14:04:18+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:58+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53", + "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:16+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "8.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:59:38+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "12.5.24", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "d75dd30597caa80e72fad2ef7904601a30ef1046" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d75dd30597caa80e72fad2ef7904601a30ef1046", + "reference": "d75dd30597caa80e72fad2ef7904601a30ef1046", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.3", + "phpunit/php-code-coverage": "^12.5.6", + "phpunit/php-file-iterator": "^6.0.1", + "phpunit/php-invoker": "^6.0.0", + "phpunit/php-text-template": "^5.0.0", + "phpunit/php-timer": "^8.0.0", + "sebastian/cli-parser": "^4.2.0", + "sebastian/comparator": "^7.1.6", + "sebastian/diff": "^7.0.0", + "sebastian/environment": "^8.1.0", + "sebastian/exporter": "^7.0.2", + "sebastian/global-state": "^8.0.2", + "sebastian/object-enumerator": "^7.0.0", + "sebastian/recursion-context": "^7.0.1", + "sebastian/type": "^6.0.3", + "sebastian/version": "^6.0.0", + "staabm/side-effects-detector": "^1.0.5" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "12.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.24" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsoring.html", + "type": "other" + } + ], + "time": "2026-05-01T04:21:04+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "4.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "reference": "90f41072d220e5c40df6e8635f5dafba2d9d4d04", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/4.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" + } + ], + "time": "2025-09-14T09:36:45+00:00" + }, + { + "name": "sebastian/comparator", + "version": "7.1.6", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "c769009dee98f494e0edc3fd4f4087501688f11e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c769009dee98f494e0edc3fd4f4087501688f11e", + "reference": "c769009dee98f494e0edc3fd4f4087501688f11e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/diff": "^7.0", + "sebastian/exporter": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/7.1.6" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2026-04-14T08:23:15+00:00" + }, + { + "name": "sebastian/complexity", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb", + "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f", + "reference": "7ab1ea946c012266ca32390913653d844ecd085f", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0", + "symfony/process": "^7.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:55:46+00:00" + }, + { + "name": "sebastian/environment", + "version": "8.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/b121608b28a13f721e76ffbbd386d08eff58f3f6", + "reference": "b121608b28a13f721e76ffbbd386d08eff58f3f6", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/8.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" + } + ], + "time": "2026-04-15T12:13:01+00:00" + }, + { + "name": "sebastian/exporter", + "version": "7.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/016951ae10980765e4e7aee491eb288c64e505b7", + "reference": "016951ae10980765e4e7aee491eb288c64e505b7", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.3", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/7.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:16:11+00:00" + }, + { + "name": "sebastian/global-state", + "version": "8.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ef1377171613d09edd25b7816f05be8313f9115d", + "reference": "ef1377171613d09edd25b7816f05be8313f9115d", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "8.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/8.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/global-state", + "type": "tidelift" + } + ], + "time": "2025-08-29T11:29:25+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.0", + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:28+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "7.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894", + "shasum": "" + }, + "require": { + "php": ">=8.3", + "sebastian/object-reflector": "^5.0", + "sebastian/recursion-context": "^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:57:48+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a", + "reference": "4bfa827c969c98be1e527abd576533293c634f6a", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T04:58:17+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "reference": "0b01998a7d5b1f122911a66bebcb8d46f0c82d8c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-13T04:44:59+00:00" + }, + { + "name": "sebastian/type", + "version": "6.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d", + "reference": "e549163b9760b8f71f191651d22acf32d56d6d4d", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "require-dev": { + "phpunit/phpunit": "^12.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:57:12+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^8.1" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-12-08T11:19:18+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": "^8.3" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} diff --git a/rebuild/config/app.php b/rebuild/config/app.php new file mode 100644 index 0000000..423eed5 --- /dev/null +++ b/rebuild/config/app.php @@ -0,0 +1,126 @@ + env('APP_NAME', 'Laravel'), + + /* + |-------------------------------------------------------------------------- + | Application Environment + |-------------------------------------------------------------------------- + | + | This value determines the "environment" your application is currently + | running in. This may determine how you prefer to configure various + | services the application utilizes. Set this in your ".env" file. + | + */ + + 'env' => env('APP_ENV', 'production'), + + /* + |-------------------------------------------------------------------------- + | Application Debug Mode + |-------------------------------------------------------------------------- + | + | When your application is in debug mode, detailed error messages with + | stack traces will be shown on every error that occurs within your + | application. If disabled, a simple generic error page is shown. + | + */ + + 'debug' => (bool) env('APP_DEBUG', false), + + /* + |-------------------------------------------------------------------------- + | Application URL + |-------------------------------------------------------------------------- + | + | This URL is used by the console to properly generate URLs when using + | the Artisan command line tool. You should set this to the root of + | the application so that it's available within Artisan commands. + | + */ + + 'url' => env('APP_URL', 'http://localhost'), + + /* + |-------------------------------------------------------------------------- + | Application Timezone + |-------------------------------------------------------------------------- + | + | Here you may specify the default timezone for your application, which + | will be used by the PHP date and date-time functions. The timezone + | is set to "UTC" by default as it is suitable for most use cases. + | + */ + + 'timezone' => 'UTC', + + /* + |-------------------------------------------------------------------------- + | Application Locale Configuration + |-------------------------------------------------------------------------- + | + | The application locale determines the default locale that will be used + | by Laravel's translation / localization methods. This option can be + | set to any locale for which you plan to have translation strings. + | + */ + + 'locale' => env('APP_LOCALE', 'en'), + + 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), + + 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + /* + |-------------------------------------------------------------------------- + | Encryption Key + |-------------------------------------------------------------------------- + | + | This key is utilized by Laravel's encryption services and should be set + | to a random, 32 character string to ensure that all encrypted values + | are secure. You should do this prior to deploying the application. + | + */ + + 'cipher' => 'AES-256-CBC', + + 'key' => env('APP_KEY'), + + 'previous_keys' => [ + ...array_filter( + explode(',', (string) env('APP_PREVIOUS_KEYS', '')) + ), + ], + + /* + |-------------------------------------------------------------------------- + | Maintenance Mode Driver + |-------------------------------------------------------------------------- + | + | These configuration options determine the driver used to determine and + | manage Laravel's "maintenance mode" status. The "cache" driver will + | allow maintenance mode to be controlled across multiple machines. + | + | Supported drivers: "file", "cache" + | + */ + + 'maintenance' => [ + 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), + 'store' => env('APP_MAINTENANCE_STORE', 'database'), + ], + +]; diff --git a/rebuild/config/auth.php b/rebuild/config/auth.php new file mode 100644 index 0000000..d7568ff --- /dev/null +++ b/rebuild/config/auth.php @@ -0,0 +1,117 @@ + [ + 'guard' => env('AUTH_GUARD', 'web'), + 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), + ], + + /* + |-------------------------------------------------------------------------- + | Authentication Guards + |-------------------------------------------------------------------------- + | + | Next, you may define every authentication guard for your application. + | Of course, a great default configuration has been defined for you + | which utilizes session storage plus the Eloquent user provider. + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | Supported: "session" + | + */ + + 'guards' => [ + 'web' => [ + 'driver' => 'session', + 'provider' => 'users', + ], + ], + + /* + |-------------------------------------------------------------------------- + | User Providers + |-------------------------------------------------------------------------- + | + | All authentication guards have a user provider, which defines how the + | users are actually retrieved out of your database or other storage + | system used by the application. Typically, Eloquent is utilized. + | + | If you have multiple user tables or models you may configure multiple + | providers to represent the model / table. These providers may then + | be assigned to any extra authentication guards you have defined. + | + | Supported: "database", "eloquent" + | + */ + + 'providers' => [ + 'users' => [ + 'driver' => 'eloquent', + 'model' => env('AUTH_MODEL', User::class), + ], + + // 'users' => [ + // 'driver' => 'database', + // 'table' => 'users', + // ], + ], + + /* + |-------------------------------------------------------------------------- + | Resetting Passwords + |-------------------------------------------------------------------------- + | + | These configuration options specify the behavior of Laravel's password + | reset functionality, including the table utilized for token storage + | and the user provider that is invoked to actually retrieve users. + | + | The expiry time is the number of minutes that each reset token will be + | considered valid. This security feature keeps tokens short-lived so + | they have less time to be guessed. You may change this as needed. + | + | The throttle setting is the number of seconds a user must wait before + | generating more password reset tokens. This prevents the user from + | quickly generating a very large amount of password reset tokens. + | + */ + + 'passwords' => [ + 'users' => [ + 'provider' => 'users', + 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), + 'expire' => 60, + 'throttle' => 60, + ], + ], + + /* + |-------------------------------------------------------------------------- + | Password Confirmation Timeout + |-------------------------------------------------------------------------- + | + | Here you may define the number of seconds before a password confirmation + | window expires and users are asked to re-enter their password via the + | confirmation screen. By default, the timeout lasts for three hours. + | + */ + + 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), + +]; diff --git a/rebuild/config/cache.php b/rebuild/config/cache.php new file mode 100644 index 0000000..c68acdf --- /dev/null +++ b/rebuild/config/cache.php @@ -0,0 +1,130 @@ + env('CACHE_STORE', 'database'), + + /* + |-------------------------------------------------------------------------- + | Cache Stores + |-------------------------------------------------------------------------- + | + | Here you may define all of the cache "stores" for your application as + | well as their drivers. You may even define multiple stores for the + | same cache driver to group types of items stored in your caches. + | + | Supported drivers: "array", "database", "file", "memcached", + | "redis", "dynamodb", "octane", + | "failover", "null" + | + */ + + 'stores' => [ + + 'array' => [ + 'driver' => 'array', + 'serialize' => false, + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_CACHE_CONNECTION'), + 'table' => env('DB_CACHE_TABLE', 'cache'), + 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), + 'lock_table' => env('DB_CACHE_LOCK_TABLE'), + ], + + 'file' => [ + 'driver' => 'file', + 'path' => storage_path('framework/cache/data'), + 'lock_path' => storage_path('framework/cache/data'), + ], + + 'memcached' => [ + 'driver' => 'memcached', + 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), + 'sasl' => [ + env('MEMCACHED_USERNAME'), + env('MEMCACHED_PASSWORD'), + ], + 'options' => [ + // Memcached::OPT_CONNECT_TIMEOUT => 2000, + ], + 'servers' => [ + [ + 'host' => env('MEMCACHED_HOST', '127.0.0.1'), + 'port' => env('MEMCACHED_PORT', 11211), + 'weight' => 100, + ], + ], + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), + 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), + ], + + 'dynamodb' => [ + 'driver' => 'dynamodb', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), + 'endpoint' => env('DYNAMODB_ENDPOINT'), + ], + + 'octane' => [ + 'driver' => 'octane', + ], + + 'failover' => [ + 'driver' => 'failover', + 'stores' => [ + 'database', + 'array', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Cache Key Prefix + |-------------------------------------------------------------------------- + | + | When utilizing the APC, database, memcached, Redis, and DynamoDB cache + | stores, there might be other applications using the same cache. For + | that reason, you may prefix every cache key to avoid collisions. + | + */ + + 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), + + /* + |-------------------------------------------------------------------------- + | Serializable Classes + |-------------------------------------------------------------------------- + | + | This value determines the classes that can be unserialized from cache + | storage. By default, no PHP classes will be unserialized from your + | cache to prevent gadget chain attacks if your APP_KEY is leaked. + | + */ + + 'serializable_classes' => false, + +]; diff --git a/rebuild/config/database.php b/rebuild/config/database.php new file mode 100644 index 0000000..64709ce --- /dev/null +++ b/rebuild/config/database.php @@ -0,0 +1,184 @@ + env('DB_CONNECTION', 'sqlite'), + + /* + |-------------------------------------------------------------------------- + | Database Connections + |-------------------------------------------------------------------------- + | + | Below are all of the database connections defined for your application. + | An example configuration is provided for each database system which + | is supported by Laravel. You're free to add / remove connections. + | + */ + + 'connections' => [ + + 'sqlite' => [ + 'driver' => 'sqlite', + 'url' => env('DB_URL'), + 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'prefix' => '', + 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), + 'busy_timeout' => null, + 'journal_mode' => null, + 'synchronous' => null, + 'transaction_mode' => 'DEFERRED', + ], + + 'mysql' => [ + 'driver' => 'mysql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'mariadb' => [ + 'driver' => 'mariadb', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'unix_socket' => env('DB_SOCKET', ''), + 'charset' => env('DB_CHARSET', 'utf8mb4'), + 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => true, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '5432'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + 'search_path' => 'public', + 'sslmode' => env('DB_SSLMODE', 'prefer'), + ], + + 'sqlsrv' => [ + 'driver' => 'sqlsrv', + 'url' => env('DB_URL'), + 'host' => env('DB_HOST', 'localhost'), + 'port' => env('DB_PORT', '1433'), + 'database' => env('DB_DATABASE', 'laravel'), + 'username' => env('DB_USERNAME', 'root'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => env('DB_CHARSET', 'utf8'), + 'prefix' => '', + 'prefix_indexes' => true, + // 'encrypt' => env('DB_ENCRYPT', 'yes'), + // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Migration Repository Table + |-------------------------------------------------------------------------- + | + | This table keeps track of all the migrations that have already run for + | your application. Using this information, we can determine which of + | the migrations on disk haven't actually been run on the database. + | + */ + + 'migrations' => [ + 'table' => 'migrations', + 'update_date_on_publish' => true, + ], + + /* + |-------------------------------------------------------------------------- + | Redis Databases + |-------------------------------------------------------------------------- + | + | Redis is an open source, fast, and advanced key-value store that also + | provides a richer body of commands than a typical key-value system + | such as Memcached. You may define your connection settings here. + | + */ + + 'redis' => [ + + 'client' => env('REDIS_CLIENT', 'phpredis'), + + 'options' => [ + 'cluster' => env('REDIS_CLUSTER', 'redis'), + 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), + 'persistent' => env('REDIS_PERSISTENT', false), + ], + + 'default' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_DB', '0'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), + ], + + 'cache' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'port' => env('REDIS_PORT', '6379'), + 'database' => env('REDIS_CACHE_DB', '1'), + 'max_retries' => env('REDIS_MAX_RETRIES', 3), + 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), + 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), + 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), + ], + + ], + +]; diff --git a/rebuild/config/filesystems.php b/rebuild/config/filesystems.php new file mode 100644 index 0000000..37d8fca --- /dev/null +++ b/rebuild/config/filesystems.php @@ -0,0 +1,80 @@ + env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Filesystem Disks + |-------------------------------------------------------------------------- + | + | Below you may configure as many filesystem disks as necessary, and you + | may even configure multiple disks for the same driver. Examples for + | most supported storage drivers are configured here for reference. + | + | Supported drivers: "local", "ftp", "sftp", "s3" + | + */ + + 'disks' => [ + + 'local' => [ + 'driver' => 'local', + 'root' => storage_path('app/private'), + 'serve' => true, + 'throw' => false, + 'report' => false, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage', + 'visibility' => 'public', + 'throw' => false, + 'report' => false, + ], + + 's3' => [ + 'driver' => 's3', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION'), + 'bucket' => env('AWS_BUCKET'), + 'url' => env('AWS_URL'), + 'endpoint' => env('AWS_ENDPOINT'), + 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), + 'throw' => false, + 'report' => false, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Symbolic Links + |-------------------------------------------------------------------------- + | + | Here you may configure the symbolic links that will be created when the + | `storage:link` Artisan command is executed. The array keys should be + | the locations of the links and the values should be their targets. + | + */ + + 'links' => [ + public_path('storage') => storage_path('app/public'), + ], + +]; diff --git a/rebuild/config/logging.php b/rebuild/config/logging.php new file mode 100644 index 0000000..b09cb25 --- /dev/null +++ b/rebuild/config/logging.php @@ -0,0 +1,132 @@ + env('LOG_CHANNEL', 'stack'), + + /* + |-------------------------------------------------------------------------- + | Deprecations Log Channel + |-------------------------------------------------------------------------- + | + | This option controls the log channel that should be used to log warnings + | regarding deprecated PHP and library features. This allows you to get + | your application ready for upcoming major versions of dependencies. + | + */ + + 'deprecations' => [ + 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), + 'trace' => env('LOG_DEPRECATIONS_TRACE', false), + ], + + /* + |-------------------------------------------------------------------------- + | Log Channels + |-------------------------------------------------------------------------- + | + | Here you may configure the log channels for your application. Laravel + | utilizes the Monolog PHP logging library, which includes a variety + | of powerful log handlers and formatters that you're free to use. + | + | Available drivers: "single", "daily", "slack", "syslog", + | "errorlog", "monolog", "custom", "stack" + | + */ + + 'channels' => [ + + 'stack' => [ + 'driver' => 'stack', + 'channels' => explode(',', (string) env('LOG_STACK', 'single')), + 'ignore_exceptions' => false, + ], + + 'single' => [ + 'driver' => 'single', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'daily' => [ + 'driver' => 'daily', + 'path' => storage_path('logs/laravel.log'), + 'level' => env('LOG_LEVEL', 'debug'), + 'days' => env('LOG_DAILY_DAYS', 14), + 'replace_placeholders' => true, + ], + + 'slack' => [ + 'driver' => 'slack', + 'url' => env('LOG_SLACK_WEBHOOK_URL'), + 'username' => env('LOG_SLACK_USERNAME', env('APP_NAME', 'Laravel')), + 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), + 'level' => env('LOG_LEVEL', 'critical'), + 'replace_placeholders' => true, + ], + + 'papertrail' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), + 'handler_with' => [ + 'host' => env('PAPERTRAIL_URL'), + 'port' => env('PAPERTRAIL_PORT'), + 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), + ], + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'stderr' => [ + 'driver' => 'monolog', + 'level' => env('LOG_LEVEL', 'debug'), + 'handler' => StreamHandler::class, + 'handler_with' => [ + 'stream' => 'php://stderr', + ], + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'processors' => [PsrLogMessageProcessor::class], + ], + + 'syslog' => [ + 'driver' => 'syslog', + 'level' => env('LOG_LEVEL', 'debug'), + 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), + 'replace_placeholders' => true, + ], + + 'errorlog' => [ + 'driver' => 'errorlog', + 'level' => env('LOG_LEVEL', 'debug'), + 'replace_placeholders' => true, + ], + + 'null' => [ + 'driver' => 'monolog', + 'handler' => NullHandler::class, + ], + + 'emergency' => [ + 'path' => storage_path('logs/laravel.log'), + ], + + ], + +]; diff --git a/rebuild/config/mail.php b/rebuild/config/mail.php new file mode 100644 index 0000000..e32e88d --- /dev/null +++ b/rebuild/config/mail.php @@ -0,0 +1,118 @@ + env('MAIL_MAILER', 'log'), + + /* + |-------------------------------------------------------------------------- + | Mailer Configurations + |-------------------------------------------------------------------------- + | + | Here you may configure all of the mailers used by your application plus + | their respective settings. Several examples have been configured for + | you and you are free to add your own as your application requires. + | + | Laravel supports a variety of mail "transport" drivers that can be used + | when delivering an email. You may specify which one you're using for + | your mailers below. You may also add additional mailers if needed. + | + | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", + | "postmark", "resend", "log", "array", + | "failover", "roundrobin" + | + */ + + 'mailers' => [ + + 'smtp' => [ + 'transport' => 'smtp', + 'scheme' => env('MAIL_SCHEME'), + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), + ], + + 'ses' => [ + 'transport' => 'ses', + ], + + 'postmark' => [ + 'transport' => 'postmark', + // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), + // 'client' => [ + // 'timeout' => 5, + // ], + ], + + 'resend' => [ + 'transport' => 'resend', + ], + + 'sendmail' => [ + 'transport' => 'sendmail', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), + ], + + 'log' => [ + 'transport' => 'log', + 'channel' => env('MAIL_LOG_CHANNEL'), + ], + + 'array' => [ + 'transport' => 'array', + ], + + 'failover' => [ + 'transport' => 'failover', + 'mailers' => [ + 'smtp', + 'log', + ], + 'retry_after' => 60, + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + 'retry_after' => 60, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global "From" Address + |-------------------------------------------------------------------------- + | + | You may wish for all emails sent by your application to be sent from + | the same address. Here you may specify a name and address that is + | used globally for all emails that are sent by your application. + | + */ + + 'from' => [ + 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), + 'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Laravel')), + ], + +]; diff --git a/rebuild/config/queue.php b/rebuild/config/queue.php new file mode 100644 index 0000000..79c2c0a --- /dev/null +++ b/rebuild/config/queue.php @@ -0,0 +1,129 @@ + env('QUEUE_CONNECTION', 'database'), + + /* + |-------------------------------------------------------------------------- + | Queue Connections + |-------------------------------------------------------------------------- + | + | Here you may configure the connection options for every queue backend + | used by your application. An example configuration is provided for + | each backend supported by Laravel. You're also free to add more. + | + | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", + | "deferred", "background", "failover", "null" + | + */ + + 'connections' => [ + + 'sync' => [ + 'driver' => 'sync', + ], + + 'database' => [ + 'driver' => 'database', + 'connection' => env('DB_QUEUE_CONNECTION'), + 'table' => env('DB_QUEUE_TABLE', 'jobs'), + 'queue' => env('DB_QUEUE', 'default'), + 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), + 'after_commit' => false, + ], + + 'beanstalkd' => [ + 'driver' => 'beanstalkd', + 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), + 'queue' => env('BEANSTALKD_QUEUE', 'default'), + 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), + 'block_for' => 0, + 'after_commit' => false, + ], + + 'sqs' => [ + 'driver' => 'sqs', + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), + 'queue' => env('SQS_QUEUE', 'default'), + 'suffix' => env('SQS_SUFFIX'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + 'after_commit' => false, + ], + + 'redis' => [ + 'driver' => 'redis', + 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), + 'queue' => env('REDIS_QUEUE', 'default'), + 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), + 'block_for' => null, + 'after_commit' => false, + ], + + 'deferred' => [ + 'driver' => 'deferred', + ], + + 'background' => [ + 'driver' => 'background', + ], + + 'failover' => [ + 'driver' => 'failover', + 'connections' => [ + 'database', + 'deferred', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Job Batching + |-------------------------------------------------------------------------- + | + | The following options configure the database and table that store job + | batching information. These options can be updated to any database + | connection and table which has been defined by your application. + | + */ + + 'batching' => [ + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'job_batches', + ], + + /* + |-------------------------------------------------------------------------- + | Failed Queue Jobs + |-------------------------------------------------------------------------- + | + | These options configure the behavior of failed queue job logging so you + | can control how and where failed jobs are stored. Laravel ships with + | support for storing failed jobs in a simple file or in a database. + | + | Supported drivers: "database-uuids", "dynamodb", "file", "null" + | + */ + + 'failed' => [ + 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), + 'database' => env('DB_CONNECTION', 'sqlite'), + 'table' => 'failed_jobs', + ], + +]; diff --git a/rebuild/config/services.php b/rebuild/config/services.php new file mode 100644 index 0000000..6a90eb8 --- /dev/null +++ b/rebuild/config/services.php @@ -0,0 +1,38 @@ + [ + 'key' => env('POSTMARK_API_KEY'), + ], + + 'resend' => [ + 'key' => env('RESEND_API_KEY'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + +]; diff --git a/rebuild/config/session.php b/rebuild/config/session.php new file mode 100644 index 0000000..f574482 --- /dev/null +++ b/rebuild/config/session.php @@ -0,0 +1,233 @@ + env('SESSION_DRIVER', 'database'), + + /* + |-------------------------------------------------------------------------- + | Session Lifetime + |-------------------------------------------------------------------------- + | + | Here you may specify the number of minutes that you wish the session + | to be allowed to remain idle before it expires. If you want them + | to expire immediately when the browser is closed then you may + | indicate that via the expire_on_close configuration option. + | + */ + + 'lifetime' => (int) env('SESSION_LIFETIME', 120), + + 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), + + /* + |-------------------------------------------------------------------------- + | Session Encryption + |-------------------------------------------------------------------------- + | + | This option allows you to easily specify that all of your session data + | should be encrypted before it's stored. All encryption is performed + | automatically by Laravel and you may use the session like normal. + | + */ + + 'encrypt' => env('SESSION_ENCRYPT', false), + + /* + |-------------------------------------------------------------------------- + | Session File Location + |-------------------------------------------------------------------------- + | + | When utilizing the "file" session driver, the session files are placed + | on disk. The default storage location is defined here; however, you + | are free to provide another location where they should be stored. + | + */ + + 'files' => storage_path('framework/sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Database Connection + |-------------------------------------------------------------------------- + | + | When using the "database" or "redis" session drivers, you may specify a + | connection that should be used to manage these sessions. This should + | correspond to a connection in your database configuration options. + | + */ + + 'connection' => env('SESSION_CONNECTION'), + + /* + |-------------------------------------------------------------------------- + | Session Database Table + |-------------------------------------------------------------------------- + | + | When using the "database" session driver, you may specify the table to + | be used to store sessions. Of course, a sensible default is defined + | for you; however, you're welcome to change this to another table. + | + */ + + 'table' => env('SESSION_TABLE', 'sessions'), + + /* + |-------------------------------------------------------------------------- + | Session Cache Store + |-------------------------------------------------------------------------- + | + | When using one of the framework's cache driven session backends, you may + | define the cache store which should be used to store the session data + | between requests. This must match one of your defined cache stores. + | + | Affects: "dynamodb", "memcached", "redis" + | + */ + + 'store' => env('SESSION_STORE'), + + /* + |-------------------------------------------------------------------------- + | Session Sweeping Lottery + |-------------------------------------------------------------------------- + | + | Some session drivers must manually sweep their storage location to get + | rid of old sessions from storage. Here are the chances that it will + | happen on a given request. By default, the odds are 2 out of 100. + | + */ + + 'lottery' => [2, 100], + + /* + |-------------------------------------------------------------------------- + | Session Cookie Name + |-------------------------------------------------------------------------- + | + | Here you may change the name of the session cookie that is created by + | the framework. Typically, you should not need to change this value + | since doing so does not grant a meaningful security improvement. + | + */ + + 'cookie' => env( + 'SESSION_COOKIE', + Str::slug((string) env('APP_NAME', 'laravel')).'-session' + ), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Path + |-------------------------------------------------------------------------- + | + | The session cookie path determines the path for which the cookie will + | be regarded as available. Typically, this will be the root path of + | your application, but you're free to change this when necessary. + | + */ + + 'path' => env('SESSION_PATH', '/'), + + /* + |-------------------------------------------------------------------------- + | Session Cookie Domain + |-------------------------------------------------------------------------- + | + | This value determines the domain and subdomains the session cookie is + | available to. By default, the cookie will be available to the root + | domain without subdomains. Typically, this shouldn't be changed. + | + */ + + 'domain' => env('SESSION_DOMAIN'), + + /* + |-------------------------------------------------------------------------- + | HTTPS Only Cookies + |-------------------------------------------------------------------------- + | + | By setting this option to true, session cookies will only be sent back + | to the server if the browser has a HTTPS connection. This will keep + | the cookie from being sent to you when it can't be done securely. + | + */ + + 'secure' => env('SESSION_SECURE_COOKIE'), + + /* + |-------------------------------------------------------------------------- + | HTTP Access Only + |-------------------------------------------------------------------------- + | + | Setting this value to true will prevent JavaScript from accessing the + | value of the cookie and the cookie will only be accessible through + | the HTTP protocol. It's unlikely you should disable this option. + | + */ + + 'http_only' => env('SESSION_HTTP_ONLY', true), + + /* + |-------------------------------------------------------------------------- + | Same-Site Cookies + |-------------------------------------------------------------------------- + | + | This option determines how your cookies behave when cross-site requests + | take place, and can be used to mitigate CSRF attacks. By default, we + | will set this value to "lax" to permit secure cross-site requests. + | + | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value + | + | Supported: "lax", "strict", "none", null + | + */ + + 'same_site' => env('SESSION_SAME_SITE', 'lax'), + + /* + |-------------------------------------------------------------------------- + | Partitioned Cookies + |-------------------------------------------------------------------------- + | + | Setting this value to true will tie the cookie to the top-level site for + | a cross-site context. Partitioned cookies are accepted by the browser + | when flagged "secure" and the Same-Site attribute is set to "none". + | + */ + + 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), + + /* + |-------------------------------------------------------------------------- + | Session Serialization + |-------------------------------------------------------------------------- + | + | This value controls the serialization strategy for session data, which + | is JSON by default. Setting this to "php" allows the storage of PHP + | objects in the session but can make an application vulnerable to + | "gadget chain" serialization attacks if the APP_KEY is leaked. + | + | Supported: "json", "php" + | + */ + + 'serialization' => 'json', + +]; diff --git a/rebuild/database/.gitignore b/rebuild/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/rebuild/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/rebuild/database/factories/UserFactory.php b/rebuild/database/factories/UserFactory.php new file mode 100644 index 0000000..c4ceb07 --- /dev/null +++ b/rebuild/database/factories/UserFactory.php @@ -0,0 +1,45 @@ + + */ +class UserFactory extends Factory +{ + /** + * The current password being used by the factory. + */ + protected static ?string $password; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => fake()->name(), + 'email' => fake()->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => static::$password ??= Hash::make('password'), + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + */ + public function unverified(): static + { + return $this->state(fn (array $attributes) => [ + 'email_verified_at' => null, + ]); + } +} diff --git a/rebuild/database/migrations/0001_01_01_000000_create_users_table.php b/rebuild/database/migrations/0001_01_01_000000_create_users_table.php new file mode 100644 index 0000000..05fb5d9 --- /dev/null +++ b/rebuild/database/migrations/0001_01_01_000000_create_users_table.php @@ -0,0 +1,49 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->timestamp('email_verified_at')->nullable(); + $table->string('password'); + $table->rememberToken(); + $table->timestamps(); + }); + + Schema::create('password_reset_tokens', function (Blueprint $table) { + $table->string('email')->primary(); + $table->string('token'); + $table->timestamp('created_at')->nullable(); + }); + + Schema::create('sessions', function (Blueprint $table) { + $table->string('id')->primary(); + $table->foreignId('user_id')->nullable()->index(); + $table->string('ip_address', 45)->nullable(); + $table->text('user_agent')->nullable(); + $table->longText('payload'); + $table->integer('last_activity')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + Schema::dropIfExists('password_reset_tokens'); + Schema::dropIfExists('sessions'); + } +}; diff --git a/rebuild/database/migrations/0001_01_01_000001_create_cache_table.php b/rebuild/database/migrations/0001_01_01_000001_create_cache_table.php new file mode 100644 index 0000000..06dc7a5 --- /dev/null +++ b/rebuild/database/migrations/0001_01_01_000001_create_cache_table.php @@ -0,0 +1,35 @@ +string('key')->primary(); + $table->mediumText('value'); + $table->bigInteger('expiration')->index(); + }); + + Schema::create('cache_locks', function (Blueprint $table) { + $table->string('key')->primary(); + $table->string('owner'); + $table->bigInteger('expiration')->index(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cache'); + Schema::dropIfExists('cache_locks'); + } +}; diff --git a/rebuild/database/migrations/0001_01_01_000002_create_jobs_table.php b/rebuild/database/migrations/0001_01_01_000002_create_jobs_table.php new file mode 100644 index 0000000..87d3ed7 --- /dev/null +++ b/rebuild/database/migrations/0001_01_01_000002_create_jobs_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedSmallInteger('attempts'); + $table->unsignedInteger('reserved_at')->nullable(); + $table->unsignedInteger('available_at'); + $table->unsignedInteger('created_at'); + }); + + Schema::create('job_batches', function (Blueprint $table) { + $table->string('id')->primary(); + $table->string('name'); + $table->integer('total_jobs'); + $table->integer('pending_jobs'); + $table->integer('failed_jobs'); + $table->longText('failed_job_ids'); + $table->mediumText('options')->nullable(); + $table->integer('cancelled_at')->nullable(); + $table->integer('created_at'); + $table->integer('finished_at')->nullable(); + }); + + Schema::create('failed_jobs', function (Blueprint $table) { + $table->id(); + $table->string('uuid')->unique(); + $table->text('connection'); + $table->text('queue'); + $table->longText('payload'); + $table->longText('exception'); + $table->timestamp('failed_at')->useCurrent(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jobs'); + Schema::dropIfExists('job_batches'); + Schema::dropIfExists('failed_jobs'); + } +}; diff --git a/rebuild/database/seeders/DatabaseSeeder.php b/rebuild/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..6b901f8 --- /dev/null +++ b/rebuild/database/seeders/DatabaseSeeder.php @@ -0,0 +1,25 @@ +create(); + + User::factory()->create([ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + } +} diff --git a/rebuild/package-lock.json b/rebuild/package-lock.json new file mode 100644 index 0000000..a37511b --- /dev/null +++ b/rebuild/package-lock.json @@ -0,0 +1,1618 @@ +{ + "name": "rebuild", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^3.0.0", + "tailwindcss": "^4.0.0", + "vite": "^8.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.127.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz", + "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.17.tgz", + "integrity": "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.17.tgz", + "integrity": "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.17.tgz", + "integrity": "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.17.tgz", + "integrity": "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.17.tgz", + "integrity": "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.17.tgz", + "integrity": "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.17.tgz", + "integrity": "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.17.tgz", + "integrity": "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.4.tgz", + "integrity": "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.4" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.4.tgz", + "integrity": "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-x64": "4.2.4", + "@tailwindcss/oxide-freebsd-x64": "4.2.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-x64-musl": "4.2.4", + "@tailwindcss/oxide-wasm32-wasi": "4.2.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz", + "integrity": "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz", + "integrity": "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz", + "integrity": "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz", + "integrity": "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz", + "integrity": "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz", + "integrity": "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz", + "integrity": "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz", + "integrity": "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz", + "integrity": "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz", + "integrity": "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", + "integrity": "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz", + "integrity": "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.4.tgz", + "integrity": "sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.4", + "@tailwindcss/oxide": "4.2.4", + "tailwindcss": "4.2.4" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/concurrently": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.21.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.0.tgz", + "integrity": "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/laravel-vite-plugin": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-3.1.0.tgz", + "integrity": "sha512-Fzocl+X4eQ9jOi0RwdphYRGkUbPJ3ky1pTAST5Ot18cS2gw6d2vldK2eCrlKDVjtibCjCx5qptYDlA0373n7qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "tinyglobby": "^0.2.12", + "vite-plugin-full-reload": "^1.1.0" + }, + "bin": { + "clean-orphaned-assets": "bin/clean.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "fontaine": "^0.5.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "fontaine": { + "optional": true + } + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", + "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.0.0-rc.17", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", + "integrity": "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.127.0", + "@rolldown/pluginutils": "1.0.0-rc.17" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", + "@rolldown/binding-darwin-x64": "1.0.0-rc.17", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", + "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/vite": { + "version": "8.0.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.10.tgz", + "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.10", + "rolldown": "1.0.0-rc.17", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-full-reload": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-plugin-full-reload/-/vite-plugin-full-reload-1.2.0.tgz", + "integrity": "sha512-kz18NW79x0IHbxRSHm0jttP4zoO9P9gXh+n6UTwlNKnviTTEpOlum6oS9SmecrTtSr+muHEn5TUuC75UovQzcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "picomatch": "^2.3.1" + } + }, + "node_modules/vite-plugin-full-reload/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/rebuild/package.json b/rebuild/package.json new file mode 100644 index 0000000..296ac3f --- /dev/null +++ b/rebuild/package.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://www.schemastore.org/package.json", + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^3.0.0", + "tailwindcss": "^4.0.0", + "vite": "^8.0.0" + } +} diff --git a/rebuild/phpunit.xml b/rebuild/phpunit.xml new file mode 100644 index 0000000..e7f0a48 --- /dev/null +++ b/rebuild/phpunit.xml @@ -0,0 +1,36 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + + + + diff --git a/rebuild/public/.htaccess b/rebuild/public/.htaccess new file mode 100644 index 0000000..b574a59 --- /dev/null +++ b/rebuild/public/.htaccess @@ -0,0 +1,25 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Handle X-XSRF-Token Header + RewriteCond %{HTTP:x-xsrf-token} . + RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/rebuild/public/favicon.ico b/rebuild/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/rebuild/public/index.php b/rebuild/public/index.php new file mode 100644 index 0000000..ee8f07e --- /dev/null +++ b/rebuild/public/index.php @@ -0,0 +1,20 @@ +handleRequest(Request::capture()); diff --git a/rebuild/public/robots.txt b/rebuild/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/rebuild/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/rebuild/resources/css/app.css b/rebuild/resources/css/app.css new file mode 100644 index 0000000..c3f620a --- /dev/null +++ b/rebuild/resources/css/app.css @@ -0,0 +1,91 @@ +@import 'tailwindcss'; + +@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'; +@source '../../storage/framework/views/*.php'; +@source '../**/*.blade.php'; +@source '../**/*.js'; + +@theme { + --font-sans: 'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', + 'Segoe UI Symbol', 'Noto Color Emoji'; +} + +@layer base { + html { + scroll-behavior: smooth; + } + + body { + font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11'; + } + + ::selection { + background: rgba(15, 23, 42, 0.12); + } +} + +@layer components { + .section-card { + @apply rounded-xl bg-white shadow-[0_8px_12px_rgba(13,10,44,0.04)]; + } + + .muted-label { + @apply text-sm font-semibold text-[#2D68F8]; + } + + .metric-card { + @apply rounded-[20px] border border-slate-200 bg-slate-50 p-5; + } + + .template-input { + @apply w-full rounded-md border border-[#D1D5DB] bg-white px-6 py-3.5 text-[#15171A] outline-hidden duration-200 placeholder:text-[#979797] focus:border-transparent focus:ring-2 focus:ring-[#5C6A78]/20; + } + + .template-button-dark { + @apply inline-flex items-center justify-center rounded-md bg-[#15171A] px-5 py-3.5 font-medium text-white transition duration-200 hover:opacity-90; + } + + .dashboard-shell { + @apply mx-auto flex max-w-[1400px] gap-6 px-4 pb-10 pt-24 sm:px-6 xl:px-8; + } + + .dashboard-sidebar { + @apply hidden w-full max-w-[290px] shrink-0 lg:block; + } + + .dashboard-sidebar-card { + @apply sticky top-24 rounded-xl border border-[#E5E7EB] bg-white p-5 shadow-[0_8px_12px_rgba(13,10,44,0.04)]; + } + + .dashboard-sidebar-link { + @apply flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm text-[#374151] transition hover:bg-[#F9FAFB] hover:text-[#15171A]; + } + + .dashboard-sidebar-link-active { + @apply bg-[#15171A] text-white hover:bg-[#15171A] hover:text-white; + } + + .dashboard-sidebar-group { + @apply mt-5; + } + + .dashboard-sidebar-group-label { + @apply flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-semibold text-[#15171A]; + } + + .dashboard-sidebar-submenu { + @apply ml-4 mt-1 space-y-1 border-l border-[#E5E7EB] pl-3.5; + } + + .dashboard-sidebar-icon { + @apply inline-flex h-4.5 w-4.5 shrink-0 items-center justify-center text-[#7A8594]; + } + + .dashboard-sidebar-link-active .dashboard-sidebar-icon { + @apply text-white; + } + + .dashboard-content { + @apply min-w-0 flex-1; + } +} diff --git a/rebuild/resources/js/app.js b/rebuild/resources/js/app.js new file mode 100644 index 0000000..8337712 --- /dev/null +++ b/rebuild/resources/js/app.js @@ -0,0 +1 @@ +// diff --git a/rebuild/resources/views/admin/pages/mahasiswa-import.blade.php b/rebuild/resources/views/admin/pages/mahasiswa-import.blade.php new file mode 100644 index 0000000..4a7ca68 --- /dev/null +++ b/rebuild/resources/views/admin/pages/mahasiswa-import.blade.php @@ -0,0 +1,94 @@ + +
+
+
+
    +
  1. + @include('dashboard.partials.icon', ['icon' => 'home']) + Home +
  2. +
  3. /
  4. +
  5. {{ $pageTitle }}
  6. +
+

{{ $pageTitle }}

+

{{ $pageDescription }}

+
+
+
{{ $pageDate }}
+
{{ $user['username'] }} · {{ $user['nmprodi'] }}
+
+
+
+ + @if (session('error')) +
{{ session('error') }}
+ @endif + + @if ($errors->any()) +
+

Import belum bisa diproses.

+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+
+ @csrf +
+
+ + +

Maksimal 5 MB. Baris pertama harus berisi header.

+
+ +
+ + +
+ +
+ + +

Dipakai jika kolom `password` kosong atau tidak ada pada baris mahasiswa baru.

+
+ +
+ + Batal +
+
+
+ + +
+
diff --git a/rebuild/resources/views/admin/pages/pengumuman-form.blade.php b/rebuild/resources/views/admin/pages/pengumuman-form.blade.php new file mode 100644 index 0000000..da591c7 --- /dev/null +++ b/rebuild/resources/views/admin/pages/pengumuman-form.blade.php @@ -0,0 +1,68 @@ + +
+
+
+
    +
  1. + @include('dashboard.partials.icon', ['icon' => 'home']) + Home +
  2. +
  3. /
  4. +
  5. {{ $pageTitle }}
  6. +
+

{{ $pageTitle }}

+

{{ $pageDescription }}

+
+
+
{{ $pageDate }}
+
{{ $user['username'] }} · {{ $user['nmprodi'] }}
+
+
+
+ + @if ($errors->any()) +
+

Form belum bisa disimpan.

+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+ @csrf +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + Batal +
+
+
+
diff --git a/rebuild/resources/views/admin/pages/resource-form.blade.php b/rebuild/resources/views/admin/pages/resource-form.blade.php new file mode 100644 index 0000000..ffd06c3 --- /dev/null +++ b/rebuild/resources/views/admin/pages/resource-form.blade.php @@ -0,0 +1,59 @@ + +
+
+
+
    +
  1. @include('dashboard.partials.icon', ['icon' => 'home'])Home
  2. +
  3. /
  4. +
  5. {{ $pageTitle }}
  6. +
+

{{ $pageTitle }}

+

{{ $pageDescription }}

+
+
{{ $user['username'] }} · {{ $user['nmprodi'] }}
+
+
+ + @if ($errors->any()) +
+

Form belum bisa disimpan.

+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+ @csrf + @if (($method ?? 'POST') !== 'POST') + @method($method) + @endif +
+ @foreach ($fields as $field) +
+ + @if (($field['type'] ?? 'text') === 'select') + + @elseif (($field['type'] ?? 'text') === 'textarea') + + @else + + @endif + @if (!empty($field['help'])) +

{{ $field['help'] }}

+ @endif +
+ @endforeach +
+
+ + Batal +
+
+
diff --git a/rebuild/resources/views/admin/pages/table.blade.php b/rebuild/resources/views/admin/pages/table.blade.php new file mode 100644 index 0000000..c7b5ce7 --- /dev/null +++ b/rebuild/resources/views/admin/pages/table.blade.php @@ -0,0 +1,101 @@ + +
+
+
+
    +
  1. + @include('dashboard.partials.icon', ['icon' => 'home']) + Home +
  2. +
  3. /
  4. +
  5. {{ $pageTitle }}
  6. +
+

{{ $pageTitle }}

+

{{ $pageDescription }}

+
+
+
{{ $pageDate }}
+
{{ $user['username'] }} · {{ $user['nmprodi'] }}
+
+ @csrf + +
+
+
+ + @if (!empty($actions ?? [])) +
+ @foreach ($actions as $action) + {{ $action['label'] }} + @endforeach +
+ @endif +
+ + @if (session('success')) +
{{ session('success') }}
+ @endif + + @if (!empty($search ?? false)) +
+
+ + +
+
+ @endif + +
+
+ + + + + @foreach ($columns as $column) + + @endforeach + @if (!empty($rowActions ?? null)) + + @endif + + + + @forelse ($rows as $row) + + + @foreach ($map($row) as $value) + + @endforeach + @if (!empty($rowActions ?? null)) + + @endif + + @empty + + + + @endforelse + +
No.{{ $column }}Aksi
{{ method_exists($rows, 'currentPage') ? $loop->iteration + ($rows->currentPage() - 1) * $rows->perPage() : $loop->iteration }}{!! is_string($value) ? e($value) : $value !!} +
+ @foreach ($rowActions($row) as $action) + @if (($action['method'] ?? 'GET') === 'GET') + {{ $action['label'] }} + @else +
+ @csrf + @method($action['method']) + +
+ @endif + @endforeach +
+
Belum ada data.
+
+ @if (method_exists($rows, 'links')) +
+ {{ $rows->links() }} +
+ @endif +
+
diff --git a/rebuild/resources/views/auth/legacy-login.blade.php b/rebuild/resources/views/auth/legacy-login.blade.php new file mode 100644 index 0000000..d59a42e --- /dev/null +++ b/rebuild/resources/views/auth/legacy-login.blade.php @@ -0,0 +1,74 @@ + +
+
+
+
+ {{ $meta['eyebrow'] }} +

{{ $meta['heading'] }}

+

{{ $meta['description'] }}

+ +
+

{{ $meta['summary_title'] }}

+
+ @foreach ($meta['summary_points'] as $point) +
{{ $point }}
+ @endforeach +
+
+ +
+

Informasi Login

+

{{ $meta['status_note'] }}

+

{{ $meta['help_note'] }}

+
+
+ +
+ + +
+ {{ $meta['status'] }} +
+ + @if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+ @csrf +
+ + +
+ @if ($role === 'dosen') +
+ + +
+ @endif +
+ + +
+
+ {{ $role === 'mahasiswa' ? 'Gunakan akun mahasiswa aktif' : ($role === 'dosen' ? 'Gunakan akun dosen aktif' : 'Akses admin belum tersedia') }} + Kembali +
+ +
+ +

{{ $role === 'mahasiswa' ? 'Login mahasiswa tetap memakai data autentikasi SPOTA yang aktif saat ini.' : ($role === 'dosen' ? 'Login dosen membaca akun legacy aktif dengan struktur session yang mengikuti sistem lama.' : 'Halaman admin akan dibuka setelah modul utama selesai dipindahkan.') }}

+
+
+
+
+
diff --git a/rebuild/resources/views/auth/role-login.blade.php b/rebuild/resources/views/auth/role-login.blade.php new file mode 100644 index 0000000..23bad51 --- /dev/null +++ b/rebuild/resources/views/auth/role-login.blade.php @@ -0,0 +1,43 @@ + +
+
+
+
+
+
+ {{ $intro['eyebrow'] }} +

{{ $intro['heading'] }}

+

{{ $intro['description'] }}

+ +
+ @foreach ($intro['details'] as $detail) +
+ {{ $detail }} +
+ @endforeach +
+
+ + +
+
+
+
+
diff --git a/rebuild/resources/views/components/admin/partials/page-shell.blade.php b/rebuild/resources/views/components/admin/partials/page-shell.blade.php new file mode 100644 index 0000000..478ec12 --- /dev/null +++ b/rebuild/resources/views/components/admin/partials/page-shell.blade.php @@ -0,0 +1,41 @@ + +
+
+ + +
+ {{ $slot }} +
+
+
+
diff --git a/rebuild/resources/views/components/dosen/partials/page-shell.blade.php b/rebuild/resources/views/components/dosen/partials/page-shell.blade.php new file mode 100644 index 0000000..ce67c88 --- /dev/null +++ b/rebuild/resources/views/components/dosen/partials/page-shell.blade.php @@ -0,0 +1,86 @@ + +
+
+ + +
+
+
+
+
    +
  1. + @include('dashboard.partials.icon', ['icon' => 'home']) + Home +
  2. +
  3. /
  4. +
  5. {{ $pageTitle }}
  6. +
+

{{ $pageTitle }}

+

{{ $pageDescription }}

+
+
+
{{ $pageDate }}
+
{{ $user['nip'] }} · {{ $user['nmprodi'] }}
+
+ @csrf + +
+
+
+ + @if (!empty($pageActions ?? [])) +
+ @foreach ($pageActions as $action) + {{ $action['label'] }} + @endforeach +
+ @endif +
+ + @if (session('success')) +
+ {{ session('success') }} +
+ @endif + + @if (session('error')) +
+ {{ session('error') }} +
+ @endif + + {{ $slot }} +
+
+
+
diff --git a/rebuild/resources/views/components/layouts/app.blade.php b/rebuild/resources/views/components/layouts/app.blade.php new file mode 100644 index 0000000..2cca5fb --- /dev/null +++ b/rebuild/resources/views/components/layouts/app.blade.php @@ -0,0 +1,65 @@ + + + + + + {{ $title ?? 'SPOTA Rebuild' }} + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + @php + $legacyAuth = session('legacy_auth'); + $legacyRole = $legacyAuth['role'] ?? null; + $spotaLogoUrl = route('assets.spota-logo'); + $dashboardRoute = match ($legacyRole) { + 'mahasiswa' => route('dashboard.mahasiswa'), + 'dosen' => route('dashboard.dosen'), + default => null, + }; + $isDashboardShell = isset($dashboardLayout) && $dashboardLayout === true; + @endphp +
+
+
+
+ + SPOTA + +
+ +
+
+ {{ $slot }} +
+ + diff --git a/rebuild/resources/views/components/mahasiswa/partials/page-shell.blade.php b/rebuild/resources/views/components/mahasiswa/partials/page-shell.blade.php new file mode 100644 index 0000000..ba5bb6c --- /dev/null +++ b/rebuild/resources/views/components/mahasiswa/partials/page-shell.blade.php @@ -0,0 +1,97 @@ + +
+
+ + +
+
+
+
+
    +
  1. + @include('dashboard.partials.icon', ['icon' => 'home']) + Home +
  2. +
  3. /
  4. +
  5. {{ $pageTitle }}
  6. +
+

{{ $pageTitle }}

+

{{ $pageDescription }}

+
+
+
{{ $pageDate }}
+
{{ $user['nim'] }} · {{ $user['nmprodi'] }}
+
+ @csrf + +
+
+
+ + @if (!empty($pageActions ?? [])) +
+ @foreach ($pageActions as $action) + {{ $action['label'] }} + @endforeach +
+ @endif +
+ + @if (session('success')) +
+ {{ session('success') }} +
+ @endif + + @if (session('error')) +
+ {{ session('error') }} +
+ @endif + + @if ($errors->any()) +
+

Form belum bisa disimpan.

+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + + {{ $slot }} +
+
+
+
diff --git a/rebuild/resources/views/dashboard/admin.blade.php b/rebuild/resources/views/dashboard/admin.blade.php new file mode 100644 index 0000000..a62e63e --- /dev/null +++ b/rebuild/resources/views/dashboard/admin.blade.php @@ -0,0 +1,87 @@ + +
+
+
+
    +
  1. + @include('dashboard.partials.icon', ['icon' => 'home']) + Home +
  2. +
  3. /
  4. +
  5. Dashboard
  6. +
+

Dashboard Admin

+

Selamat datang di halaman administrator Sistem Pendukung Outline Tugas Akhir (SPOTA) Universitas Tanjungpura.

+
+
+
{{ $pageDate }}
+
{{ $user['username'] }} · {{ $user['nmprodi'] }}
+
+ @csrf + +
+
+
+
+ +
+ @foreach ($stats as $stat) +
+

{{ $stat['label'] }}

+

{{ $stat['value'] }}

+

{{ $stat['note'] }}

+
+ @endforeach +
+ +
+
+

Menu Kerja Administrator

+

Link di bawah mengikuti struktur fungsi administrator lama. Rebuild ini menjaga akses menuju halaman yang sudah aktif tanpa mengubah proses data yang berjalan.

+
+ @foreach ($sidebar['sections'] as $group) + + @endforeach +
+
+ +
+
+
+

Jadwal

+

Seminar/Sidang Terbaru

+
+ Kalender +
+
+ @forelse ($schedules as $schedule) +
+
+
+

{{ $schedule['jenis'] }}

+

{{ $schedule['mahasiswa'] }}

+
+ {{ $schedule['ruangan'] }} +
+

{{ $schedule['tanggal'] }}

+
+ @empty +
Belum ada jadwal seminar/sidang.
+ @endforelse +
+
+
+
diff --git a/rebuild/resources/views/dashboard/dosen.blade.php b/rebuild/resources/views/dashboard/dosen.blade.php new file mode 100644 index 0000000..9e6fae1 --- /dev/null +++ b/rebuild/resources/views/dashboard/dosen.blade.php @@ -0,0 +1,128 @@ + +
+
+

{{ $dashboard['welcomeTitle'] }}

+

{{ $dashboard['welcomeText'] }}

+ @if ($dashboard['androidLink']) + + @endif +
+
+ +
+ + + + +
+
+
+

Agenda Terdekat

+

Jadwal Seminar

+
+ {{ count($dashboard['upcomingSchedules']) }} data +
+ + @if ($dashboard['upcomingSchedules'] === []) +
+ Belum ada jadwal seminar terdekat yang terpublikasi untuk program studi ini. +
+ @else +
+ @foreach ($dashboard['upcomingSchedules'] as $schedule) +
+
+
+
+ {{ $schedule['jenis'] }} +

{{ $schedule['nama'] }}

+ {{ $schedule['nim'] }} +
+

{{ $schedule['judul'] }}

+
+
+

{{ $schedule['tanggal'] }}

+

{{ $schedule['ruangan'] }}

+
+
+
+ @endforeach +
+ @endif +
+
+ +
+
+
+
+

Kalender

+

{{ $dashboard['calendar']['monthLabel'] }}

+
+ Publikasi jadwal aktif +
+ +
+ @foreach ($dashboard['calendar']['weekdays'] as $weekday) +
{{ $weekday }}
+ @endforeach +
+ +
+ @foreach ($dashboard['calendar']['weeks'] as $week) +
+ @foreach ($week as $day) +
+
+ {{ $day['day'] }} + @if ($day['isToday']) + Hari ini + @endif +
+
+ @foreach (array_slice($day['events'], 0, 3) as $event) +
+

{{ $event['jenis'] }}

+

{{ $event['title'] }}

+
+ @endforeach +
+
+ @endforeach +
+ @endforeach +
+
+
+
diff --git a/rebuild/resources/views/dashboard/mahasiswa.blade.php b/rebuild/resources/views/dashboard/mahasiswa.blade.php new file mode 100644 index 0000000..457a65f --- /dev/null +++ b/rebuild/resources/views/dashboard/mahasiswa.blade.php @@ -0,0 +1,118 @@ + +
+
+ +
+
+
+ {{ $dashboard['eyebrow'] }} +

{{ $dashboard['title'] }}

+

{{ $dashboard['description'] }}

+
+
+
{{ $user['nim'] }} · {{ $user['nmprodi'] }}
+
+ @csrf + +
+
+
+
+ @foreach ($dashboard['stats'] as $stat) +
+

{{ $stat['label'] }}

+
+

{{ $stat['value'] }}

+ {{ $stat['delta'] }} +
+

{{ $stat['note'] }}

+
+ @endforeach +
+
+
+
+

Status Usulan

+ @if ($dashboard['latestTitle']) +

Judul terakhir: {{ $dashboard['latestTitle'] }}

+ @endif +
+

{{ $dashboard['statusAlert']['title'] }}

+

{{ $dashboard['statusAlert']['description'] }}

+ @if ($dashboard['statusAlert']['button']) + + @endif +
+
+ + +
+ +
+
+
+
+

Jadwal

+

Jadwal Terdekat

+
+ {{ $dashboard['publishedSchedules'] }} publikasi +
+ + @if ($dashboard['nextSchedule']) +
+

{{ $dashboard['nextSchedule']['jenis'] }}

+

{{ $dashboard['nextSchedule']['tanggal'] }}

+

Ruangan: {{ $dashboard['nextSchedule']['ruangan'] }}

+
+ @else +
+ Belum ada jadwal seminar yang dipublikasikan untuk mahasiswa ini. +
+ @endif +
+
+
+
+
+
+
diff --git a/rebuild/resources/views/dashboard/partials/icon.blade.php b/rebuild/resources/views/dashboard/partials/icon.blade.php new file mode 100644 index 0000000..25a70bb --- /dev/null +++ b/rebuild/resources/views/dashboard/partials/icon.blade.php @@ -0,0 +1,46 @@ +@switch($icon) + @case('home') + + @break + @case('folder') + + @break + @case('briefcase') + + @break + @case('chat') + + @break + @case('search') + + @break + @case('users') + + @break + @case('bell') + + @break + @case('megaphone') + + @break + @case('user') + + @break + @case('document') + + @break + @case('warning') + + @break + @case('clipboard') + + @break + @case('clock') + + @break + @case('chart') + + @break + @default + +@endswitch diff --git a/rebuild/resources/views/dosen/pages/bimbingan.blade.php b/rebuild/resources/views/dosen/pages/bimbingan.blade.php new file mode 100644 index 0000000..682effe --- /dev/null +++ b/rebuild/resources/views/dosen/pages/bimbingan.blade.php @@ -0,0 +1,25 @@ + +
+
+ @forelse ($bimbingan as $item) +
+
+
+

{{ $item['judul'] }}

+
+ {{ $item['mahasiswa'] }} + {{ $item['periode'] }} + {{ $item['tanggal'] }} +
+
+ +
+
+ @empty +
Belum ada data bimbingan.
+ @endforelse +
+
+
diff --git a/rebuild/resources/views/dosen/pages/cari.blade.php b/rebuild/resources/views/dosen/pages/cari.blade.php new file mode 100644 index 0000000..d44d6fa --- /dev/null +++ b/rebuild/resources/views/dosen/pages/cari.blade.php @@ -0,0 +1,34 @@ + +
+
+ + + +
+ +
+ @if ($keyword !== '' && $results === []) +
Tidak ada hasil untuk `{{ $keyword }}`.
+ @endif + + @foreach ($results as $item) +
+

{{ $item['judul'] }}

+

{{ $item['deskripsi'] }}

+
+ {{ $item['mahasiswa'] }} + {{ $item['tanggal'] }} + {{ $item['status'] }} +
+ +
+ @endforeach +
+
+
diff --git a/rebuild/resources/views/dosen/pages/daftar-usulan.blade.php b/rebuild/resources/views/dosen/pages/daftar-usulan.blade.php new file mode 100644 index 0000000..b1e02d4 --- /dev/null +++ b/rebuild/resources/views/dosen/pages/daftar-usulan.blade.php @@ -0,0 +1,26 @@ + +
+
+ @forelse ($usulan as $item) +
+
+
+

{{ $item['judul'] }}

+

{{ $item['mahasiswa'] }}

+

{{ $item['periode'] }} · {{ $item['tanggal'] }}

+
+ Review +
+
+
+
{{ $item['status'] }}
+

KK: {{ $item['kk'] }}

+
+
+
+ @empty +
Tidak ada usulan aktif.
+ @endforelse +
+
+
diff --git a/rebuild/resources/views/dosen/pages/early-warning.blade.php b/rebuild/resources/views/dosen/pages/early-warning.blade.php new file mode 100644 index 0000000..444aa33 --- /dev/null +++ b/rebuild/resources/views/dosen/pages/early-warning.blade.php @@ -0,0 +1,70 @@ + +
+
+

Ancaman DO

+

{{ $summary['dropoutCount'] }}

+

Mahasiswa angkatan lama di ambang akhir masa studi.

+
+
+

Kritis

+

{{ $summary['criticalCount'] }}

+

Tahun akhir studi dan berisiko tinggi.

+
+
+

Warning

+

{{ $summary['warningCount'] }}

+

Sudah masuk fase studi lanjut dan perlu follow-up.

+
+
+

Perlu Pantau

+

{{ $summary['watchCount'] }}

+

Mulai masuk tahun studi yang perlu perhatian.

+
+
+

Aman

+

{{ $summary['safeCount'] }}

+

Masih dalam rentang monitoring rutin.

+
+
+

Total Data

+

{{ $summary['totalCount'] }}

+

Seluruh mahasiswa bimbingan yang termonitor.

+
+
+ +
+
+ Prioritaskan mahasiswa dengan status Ancaman DO, Kritis, dan Warning. Penilaian risiko memakai angkatan mahasiswa dan umur studi, dengan fallback pembacaan angkatan dari pola NIM. +
+ +
+ @forelse ($records as $item) +
+
+
+
+

{{ $item['mahasiswa'] }}

+ {{ $item['status'] }} +
+

{{ $item['judul'] }}

+
+ Angkatan {{ $item['angkatan'] ?? '-' }} + {{ !is_null($item['tahunStudi']) ? $item['tahunStudi'].' tahun studi' : 'Tahun studi tidak diketahui' }} + {{ $item['tanggal'] }} + @if (!is_null($item['days'])) + {{ $item['days'] }} hari sejak keputusan + @endif +
+

{{ $item['warningText'] }}

+
+ +
+
+ @empty +
Belum ada data early warning.
+ @endforelse +
+
+
diff --git a/rebuild/resources/views/dosen/pages/pemberitahuan.blade.php b/rebuild/resources/views/dosen/pages/pemberitahuan.blade.php new file mode 100644 index 0000000..0dac53e --- /dev/null +++ b/rebuild/resources/views/dosen/pages/pemberitahuan.blade.php @@ -0,0 +1,19 @@ + +
+
+ @forelse ($pemberitahuan as $item) +
+

{{ \Carbon\Carbon::parse($item['tgl'])->locale('id')->translatedFormat('j F Y, H:i') }}

+

{{ $item['msg'] }}

+ @if ($item['reviewHref']) + + @endif +
+ @empty +
Tidak Ada Pemberitahuan Terbaru
+ @endforelse +
+
+
diff --git a/rebuild/resources/views/dosen/pages/penawaran-form.blade.php b/rebuild/resources/views/dosen/pages/penawaran-form.blade.php new file mode 100644 index 0000000..60a7016 --- /dev/null +++ b/rebuild/resources/views/dosen/pages/penawaran-form.blade.php @@ -0,0 +1,31 @@ + +
+
+ @csrf + @if ($formMethod !== 'POST') + @method($formMethod) + @endif + +
+ + + @error('judul_penawaran') +

{{ $message }}

+ @enderror +
+ +
+ + + @error('keterangan_penawaran') +

{{ $message }}

+ @enderror +
+ +
+ + Kembali +
+
+
+
diff --git a/rebuild/resources/views/dosen/pages/penawaran.blade.php b/rebuild/resources/views/dosen/pages/penawaran.blade.php new file mode 100644 index 0000000..50b1838 --- /dev/null +++ b/rebuild/resources/views/dosen/pages/penawaran.blade.php @@ -0,0 +1,94 @@ + +
+
+ + + + + + +
+ +
+
+ +
+ + + + + + + + + + + + + + @forelse ($penawaran as $index => $item) + + + + + + + + + + @empty + + + + @endforelse + +
No.JudulDeskripsiDitawarkan OlehStatusDiambil OlehAksi
{{ $index + 1 }} +

{{ $item['judul'] }}

+

{{ $item['waktu'] }}

+
{{ $item['deskripsi'] }}{{ $item['ditawarkanOleh'] }}{{ $item['status'] }}{{ $item['mahasiswa'] }} +
+ @if ($item['canApprove']) +
+ @csrf + +
+
+ @csrf + +
+ @endif + @if ($item['isMine']) + Edit +
+ @csrf + @method('DELETE') + +
+ @endif +
+
Mengambil Data . . .
+
+
+
diff --git a/rebuild/resources/views/dosen/pages/pengumuman-detail.blade.php b/rebuild/resources/views/dosen/pages/pengumuman-detail.blade.php new file mode 100644 index 0000000..a79840a --- /dev/null +++ b/rebuild/resources/views/dosen/pages/pengumuman-detail.blade.php @@ -0,0 +1,10 @@ + +
+

{{ $pengumumanItem->judul }}

+

Diposting {{ \Carbon\Carbon::parse($pengumumanItem->tgl)->locale('id')->translatedFormat('j F Y, H:i') }}

+
{!! $pengumumanItem->isi !!}
+
+ Kembali +
+
+
diff --git a/rebuild/resources/views/dosen/pages/pengumuman.blade.php b/rebuild/resources/views/dosen/pages/pengumuman.blade.php new file mode 100644 index 0000000..a5fa691 --- /dev/null +++ b/rebuild/resources/views/dosen/pages/pengumuman.blade.php @@ -0,0 +1,17 @@ + +
+
+ @forelse ($pengumuman as $item) + + @empty +
Belum ada pengumuman.
+ @endforelse +
+
+
diff --git a/rebuild/resources/views/dosen/pages/pra-lirs.blade.php b/rebuild/resources/views/dosen/pages/pra-lirs.blade.php new file mode 100644 index 0000000..f007f8d --- /dev/null +++ b/rebuild/resources/views/dosen/pages/pra-lirs.blade.php @@ -0,0 +1,12 @@ + +
+

Integrasi Pra LIRS

+

Modul lama mengambil data Pra LIRS dari layanan eksternal Informatika. Untuk tahap overhaul ini, jalur modul dikembalikan sebagai halaman rebuild dan sumber integrasinya dipertahankan sama agar perilaku existing tidak berubah mendadak.

+
+ Endpoint sumber: {{ $externalUrl }} +
+ +
+
diff --git a/rebuild/resources/views/dosen/pages/profile.blade.php b/rebuild/resources/views/dosen/pages/profile.blade.php new file mode 100644 index 0000000..63d0a25 --- /dev/null +++ b/rebuild/resources/views/dosen/pages/profile.blade.php @@ -0,0 +1,65 @@ + +
+
+

Edit Profil

+
+ @csrf + @method('PUT') + +
+ + + + + + + + + + + +
+ +
+ +
+
+
+ +
+

Ringkasan Akun

+
+
NIP: {{ $profile->nip ?? '-' }}
+
Jenis Dosen: {{ ($profile->jenis ?? 'D') === 'K' ? 'Ketua Program Studi' : 'Dosen' }}
+
Status Akun: {{ ($profile->status ?? 'N') === 'A' ? 'Aktif' : 'Nonaktif' }}
+
Jabatan: {{ $profile->jabatan ?: '-' }}
+
+
+ Perubahan foto profil belum dipindahkan ke rebuild. Saat ini yang sudah aktif native adalah pembaruan nama lengkap, email, nomor HP, dan password. +
+
+
+
diff --git a/rebuild/resources/views/dosen/pages/review-detail.blade.php b/rebuild/resources/views/dosen/pages/review-detail.blade.php new file mode 100644 index 0000000..f831bb7 --- /dev/null +++ b/rebuild/resources/views/dosen/pages/review-detail.blade.php @@ -0,0 +1,59 @@ + +
+
+
+
+

{{ $outline['judul'] }}

+

{{ $outline['mahasiswa'] }}

+
+ {{ $outline['periode'] }} + {{ $outline['tanggal'] }} + KK: {{ $outline['kk'] }} +
+
+
+ {{ $outline['status'] }} +
+
+ +
+ {!! $outline['deskripsi'] ?: 'Tidak ada deskripsi.' !!} +
+
+ +
+
+
+

Diskusi

+

Riwayat Review

+
+ {{ count($reviews) }} entri +
+ +
+ @forelse ($reviews as $review) +
+
+
+
+

{{ $review['author'] }}

+ {{ $review['role'] }} + {{ $review['type'] }} + @if ($review['decision']) + {{ $review['decision'] }} + @endif +
+

{{ $review['timestamp'] }}

+
+
+
+ {!! $review['body'] !!} +
+
+ @empty +
Belum ada review untuk usulan ini.
+ @endforelse +
+
+
+
diff --git a/rebuild/resources/views/dosen/pages/review-saya.blade.php b/rebuild/resources/views/dosen/pages/review-saya.blade.php new file mode 100644 index 0000000..5c34f9c --- /dev/null +++ b/rebuild/resources/views/dosen/pages/review-saya.blade.php @@ -0,0 +1,36 @@ + +
+
+ + + + + + + + + + + + + @forelse ($reviews as $item) + + + + + + + + + @empty + + + + @endforelse + +
MahasiswaJudul UsulanPeriodeTanggalStatusAksi
{{ $item['mahasiswa'] }}{{ $item['judul'] }}{{ $item['periode'] }}{{ $item['tanggal'] }}{{ $item['status'] }} + Lihat Review +
Belum ada data review.
+
+
+
diff --git a/rebuild/resources/views/dosen/pages/statistik.blade.php b/rebuild/resources/views/dosen/pages/statistik.blade.php new file mode 100644 index 0000000..199aaec --- /dev/null +++ b/rebuild/resources/views/dosen/pages/statistik.blade.php @@ -0,0 +1,43 @@ + +
+
+

Statistik Draft Praoutline

+
+ + + + + + + + + + + + + @foreach ($draftStats as $item) + + + + + + + + + @endforeach + +
SemesterProsesDisetujuiDitolakGugurTotal
{{ $item->semester }}{{ $item->proses }}{{ $item->terima }}{{ $item->tolak }}{{ $item->gugur }}{{ $item->totaldraft }}
+
+
+ +
+

Statistik Dosen

+
+

Pembimbing 1

{{ $dosenStats->pemb1 ?? 0 }}

+

Pembimbing 2

{{ $dosenStats->pemb2 ?? 0 }}

+

Penguji 1

{{ $dosenStats->peng1 ?? 0 }}

+

Penguji 2

{{ $dosenStats->peng2 ?? 0 }}

+
+
+
+
diff --git a/rebuild/resources/views/home.blade.php b/rebuild/resources/views/home.blade.php new file mode 100644 index 0000000..3aa75d1 --- /dev/null +++ b/rebuild/resources/views/home.blade.php @@ -0,0 +1,63 @@ + +
+
+
+
+
+
+
+ Teknik Informatika UNTAN +
+

+ SPOTA untuk pengajuan, review, dan monitoring tugas akhir. +

+

+ SPOTA membantu mahasiswa mengajukan outline tugas akhir, memantau status persetujuan, melihat jadwal seminar atau sidang, serta menerima pengumuman akademik. Dosen dan admin dapat meninjau usulan, mengelola penawaran judul, dan memantau proses tugas akhir dalam satu sistem. +

+ +
+ + +
+
+
+ +
+ +
+ +
+
diff --git a/rebuild/resources/views/mahasiswa/pages/penawaran-detail.blade.php b/rebuild/resources/views/mahasiswa/pages/penawaran-detail.blade.php new file mode 100644 index 0000000..449b3f2 --- /dev/null +++ b/rebuild/resources/views/mahasiswa/pages/penawaran-detail.blade.php @@ -0,0 +1,22 @@ + +
+
+
+

{{ $penawaran->judul }}

+
+ Ditawarkan oleh: {{ $penawaran->dosen ?: '-' }} + KK: {{ $penawaran->kk ?: '-' }} + {{ $penawaran->waktuInput ? \Carbon\Carbon::parse($penawaran->waktuInput)->locale('id')->translatedFormat('j F Y, H:i') : '-' }} +
+
+
+ @csrf + +
+
+ +
+ {!! $penawaran->deskripsi ?: '

Tidak ada deskripsi.

' !!} +
+
+
diff --git a/rebuild/resources/views/mahasiswa/pages/penawaran.blade.php b/rebuild/resources/views/mahasiswa/pages/penawaran.blade.php new file mode 100644 index 0000000..91c8f4b --- /dev/null +++ b/rebuild/resources/views/mahasiswa/pages/penawaran.blade.php @@ -0,0 +1,82 @@ + +
+
+
+
+ + +
+
+ + +
+ +
+
+ +
+
+ + + + + + + + + + + + + + @forelse ($penawaran as $item) + @php + $available = is_null($item->statusPengambilan) || $item->statusPengambilan === '2'; + @endphp + + + + + + + + + + @empty + + + + @endforelse + +
No.JudulDitawarkan OlehKKStatusDiambil OlehAksi
{{ $loop->iteration + ($penawaran->currentPage() - 1) * $penawaran->perPage() }} + {{ $item->judul }} +

{{ str($item->deskripsi)->stripTags()->squish()->limit(120) }}

+
{{ $item->dosen ?: '-' }}{{ $item->kk ?: '-' }} + {{ $available ? 'Belum Diambil' : 'Sudah Diambil' }} + {{ $item->diambil_oleh ?: '-' }} +
+ Lihat + @if ($available) +
+ @csrf + +
+ @endif +
+
Tidak ada penawaran judul sesuai filter.
+
+
+ {{ $penawaran->links() }} +
+
+
+
diff --git a/rebuild/resources/views/mahasiswa/pages/pengumuman-detail.blade.php b/rebuild/resources/views/mahasiswa/pages/pengumuman-detail.blade.php new file mode 100644 index 0000000..67e1a8c --- /dev/null +++ b/rebuild/resources/views/mahasiswa/pages/pengumuman-detail.blade.php @@ -0,0 +1,9 @@ + +
+

{{ $pengumuman['judul'] }}

+

{{ $pengumuman['tgl'] }}

+
+ {!! $pengumuman['isi'] !!} +
+
+
diff --git a/rebuild/resources/views/mahasiswa/pages/pengumuman.blade.php b/rebuild/resources/views/mahasiswa/pages/pengumuman.blade.php new file mode 100644 index 0000000..dfebd93 --- /dev/null +++ b/rebuild/resources/views/mahasiswa/pages/pengumuman.blade.php @@ -0,0 +1,18 @@ + +
+
+ @forelse ($pengumuman as $item) +
+ {{ $item['judul'] }} +

{{ \Carbon\Carbon::parse($item['tgl'])->locale('id')->translatedFormat('j F Y, H:i') }}

+

{{ $item['preview'] }}

+ +
+ @empty +
Belum ada pengumuman untuk mahasiswa.
+ @endforelse +
+
+
diff --git a/rebuild/resources/views/mahasiswa/pages/status-usulan.blade.php b/rebuild/resources/views/mahasiswa/pages/status-usulan.blade.php new file mode 100644 index 0000000..e8febe9 --- /dev/null +++ b/rebuild/resources/views/mahasiswa/pages/status-usulan.blade.php @@ -0,0 +1,71 @@ + +
+ @if ($outline) +
+
+
+

{{ $outline['judul'] }}

+
+ Draft #{{ $outline['id'] }} + {{ $outline['tanggal'] }} + KK: {{ $outline['kelompokKeahlian'] }} +
+
+
+ {{ $outline['status'] }} +
+
+ +
+ {!! $outline['deskripsi'] ?: 'Tidak ada deskripsi usulan.' !!} +
+ + @if ($outline['catatan']) +
+

Catatan Putusan

+

{{ $outline['catatan'] }}

+
+ @endif +
+ +
+
+
+

Diskusi

+

Riwayat Review

+
+ {{ count($reviews) }} entri +
+ +
+ @forelse ($reviews as $review) +
+
+
+
+

{{ $review['author'] }}

+ {{ $review['role'] }} + {{ $review['type'] }} + @if ($review['decision']) + {{ $review['decision'] }} + @endif +
+

{{ $review['timestamp'] }}

+
+
+
+ {!! $review['body'] !!} +
+
+ @empty +
Belum ada review untuk usulan terbaru.
+ @endforelse +
+
+ @else +
+ Belum ada draft praoutline yang tercatat untuk akun mahasiswa ini. +
+ @endif +
+
diff --git a/rebuild/resources/views/mahasiswa/pages/upload-praoutline.blade.php b/rebuild/resources/views/mahasiswa/pages/upload-praoutline.blade.php new file mode 100644 index 0000000..a376569 --- /dev/null +++ b/rebuild/resources/views/mahasiswa/pages/upload-praoutline.blade.php @@ -0,0 +1,89 @@ + + @if ($hasActiveDraft) +
+

Draft Praoutline Anda Telah Diupload

+

Anda masih memiliki draft aktif. Silakan lihat status usulan dan riwayat review sebelum mengajukan draft baru.

+ +
+ @else +
+
+

Perhatian

+

Pastikan file yang diupload berupa PDF dan draft sudah diperiksa. Jika terdapat kesalahan upload, hubungi administrator prodi.

+
+ +
+ @csrf +
+
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+

Pilihan Dosen Pembimbing

+
+ @for ($i = 1; $i <= 4; $i++) + + @endfor +
+
+ +
+ + Batal +
+
+
+
+ @endif +
diff --git a/rebuild/routes/console.php b/rebuild/routes/console.php new file mode 100644 index 0000000..3c9adf1 --- /dev/null +++ b/rebuild/routes/console.php @@ -0,0 +1,8 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote'); diff --git a/rebuild/routes/web.php b/rebuild/routes/web.php new file mode 100644 index 0000000..c03cbe1 --- /dev/null +++ b/rebuild/routes/web.php @@ -0,0 +1,119 @@ + 'SPOTA | Sistem Pendukung Outline Tugas Akhir', + 'systemStatus' => [ + ['label' => 'Mahasiswa', 'value' => 'Dashboard, praoutline, pengumuman'], + ['label' => 'Dosen', 'value' => 'Review, bimbingan, penawaran judul'], + ['label' => 'Admin', 'value' => 'Data master dan pengaturan prodi'], + ], + 'highlights' => [ + ['number' => '01', 'label' => 'Mahasiswa', 'href' => route('legacy.login', 'mahasiswa'), 'description' => 'Pantau status usulan, baca pengumuman, upload praoutline, dan booking penawaran judul.'], + ['number' => '02', 'label' => 'Dosen', 'href' => route('legacy.login', 'dosen'), 'description' => 'Kelola penawaran judul, review praoutline, lihat bimbingan, dan monitoring mahasiswa.'], + ['number' => '03', 'label' => 'Admin', 'href' => route('legacy.login', 'admin'), 'description' => 'Kelola data mahasiswa, dosen, kelompok keahlian, pengumuman, jadwal, dan pengaturan prodi.'], + ], + ]); +})->name('home'); + +Route::get('/assets/spota-logo', function () { + $logoPath = base_path('../link6.jpg'); + + abort_unless(File::exists($logoPath), 404); + + return response()->file($logoPath); +})->name('assets.spota-logo'); + +Route::get('/masuk', [LegacyAuthController::class, 'showRoleLogin'])->name('role-login'); +Route::get('/login/{role}', [LegacyAuthController::class, 'showLegacyLogin'])->name('legacy.login'); +Route::post('/login/{role}', [LegacyAuthController::class, 'authenticate'])->name('legacy.authenticate'); +Route::post('/logout', [LegacyAuthController::class, 'logout'])->name('legacy.logout'); + +Route::middleware('legacy.role:mahasiswa')->group(function () { + Route::get('/dashboard/mahasiswa', MahasiswaDashboardController::class)->name('dashboard.mahasiswa'); + Route::get('/mahasiswa/status-usulan', [MahasiswaPageController::class, 'statusUsulan'])->name('mahasiswa.status-usulan'); + Route::get('/mahasiswa/praoutline/upload', [MahasiswaPageController::class, 'uploadPraoutline'])->name('mahasiswa.praoutline.upload'); + Route::post('/mahasiswa/praoutline/upload', [MahasiswaPageController::class, 'storePraoutline'])->name('mahasiswa.praoutline.store'); + Route::get('/mahasiswa/penawaran', [MahasiswaPageController::class, 'penawaran'])->name('mahasiswa.penawaran.index'); + Route::get('/mahasiswa/penawaran/{id}', [MahasiswaPageController::class, 'showPenawaran'])->whereNumber('id')->name('mahasiswa.penawaran.show'); + Route::post('/mahasiswa/penawaran/{id}/booking', [MahasiswaPageController::class, 'bookPenawaran'])->whereNumber('id')->name('mahasiswa.penawaran.book'); + Route::get('/mahasiswa/pengumuman', [MahasiswaPageController::class, 'pengumuman'])->name('mahasiswa.pengumuman.index'); + Route::get('/mahasiswa/pengumuman/{id}', [MahasiswaPageController::class, 'showPengumuman'])->whereNumber('id')->name('mahasiswa.pengumuman.show'); +}); + +Route::middleware('legacy.role:admin')->group(function () { + Route::get('/dashboard/admin', AdminDashboardController::class)->name('dashboard.admin'); + Route::get('/admin/legacy', AdminLegacyController::class)->name('admin.legacy'); + Route::get('/admin/data/mahasiswa', [AdminPageController::class, 'mahasiswa'])->name('admin.data.mahasiswa'); + Route::get('/admin/data/mahasiswa/create', [AdminPageController::class, 'createMahasiswa'])->name('admin.data.mahasiswa.create'); + Route::post('/admin/data/mahasiswa', [AdminPageController::class, 'storeMahasiswa'])->name('admin.data.mahasiswa.store'); + Route::get('/admin/data/mahasiswa/import', [AdminPageController::class, 'importMahasiswa'])->name('admin.data.mahasiswa.import'); + Route::post('/admin/data/mahasiswa/import', [AdminPageController::class, 'storeImportMahasiswa'])->name('admin.data.mahasiswa.import.store'); + Route::get('/admin/data/mahasiswa/{id}/edit', [AdminPageController::class, 'editMahasiswa'])->whereNumber('id')->name('admin.data.mahasiswa.edit'); + Route::put('/admin/data/mahasiswa/{id}', [AdminPageController::class, 'updateMahasiswa'])->whereNumber('id')->name('admin.data.mahasiswa.update'); + Route::delete('/admin/data/mahasiswa/{id}', [AdminPageController::class, 'destroyMahasiswa'])->whereNumber('id')->name('admin.data.mahasiswa.destroy'); + Route::get('/admin/data/dosen', [AdminPageController::class, 'dosen'])->name('admin.data.dosen'); + Route::get('/admin/data/dosen/create', [AdminPageController::class, 'createDosen'])->name('admin.data.dosen.create'); + Route::post('/admin/data/dosen', [AdminPageController::class, 'storeDosen'])->name('admin.data.dosen.store'); + Route::get('/admin/data/dosen/{id}/edit', [AdminPageController::class, 'editDosen'])->whereNumber('id')->name('admin.data.dosen.edit'); + Route::put('/admin/data/dosen/{id}', [AdminPageController::class, 'updateDosen'])->whereNumber('id')->name('admin.data.dosen.update'); + Route::delete('/admin/data/dosen/{id}', [AdminPageController::class, 'destroyDosen'])->whereNumber('id')->name('admin.data.dosen.destroy'); + Route::get('/admin/data/kelompok-keahlian', [AdminPageController::class, 'kk'])->name('admin.data.kk'); + Route::get('/admin/data/kelompok-keahlian/create', [AdminPageController::class, 'createKk'])->name('admin.data.kk.create'); + Route::post('/admin/data/kelompok-keahlian', [AdminPageController::class, 'storeKk'])->name('admin.data.kk.store'); + Route::get('/admin/data/kelompok-keahlian/{id}/edit', [AdminPageController::class, 'editKk'])->whereNumber('id')->name('admin.data.kk.edit'); + Route::put('/admin/data/kelompok-keahlian/{id}', [AdminPageController::class, 'updateKk'])->whereNumber('id')->name('admin.data.kk.update'); + Route::delete('/admin/data/kelompok-keahlian/{id}', [AdminPageController::class, 'destroyKk'])->whereNumber('id')->name('admin.data.kk.destroy'); + Route::get('/admin/data/fakultas', [AdminPageController::class, 'fakultas'])->name('admin.data.fakultas'); + Route::get('/admin/data/jurusan', [AdminPageController::class, 'jurusan'])->name('admin.data.jurusan'); + Route::get('/admin/data/prodi', [AdminPageController::class, 'prodi'])->name('admin.data.prodi'); + Route::get('/admin/praoutline', [AdminPageController::class, 'praoutline'])->name('admin.praoutline.index'); + Route::get('/admin/praoutline/cari', [AdminPageController::class, 'praoutlineSearch'])->name('admin.praoutline.search'); + Route::get('/admin/praoutline/keputusan', [AdminPageController::class, 'keputusan'])->name('admin.praoutline.keputusan'); + Route::get('/admin/praoutline/kep-draft', [AdminPageController::class, 'kepDraft'])->name('admin.praoutline.kep-draft'); + Route::get('/admin/praoutline/pemberitahuan', [AdminPageController::class, 'pemberitahuan'])->name('admin.praoutline.pemberitahuan'); + Route::get('/admin/pengumuman', [AdminPageController::class, 'pengumuman'])->name('admin.pengumuman.index'); + Route::get('/admin/pengumuman/create', [AdminPageController::class, 'createPengumuman'])->name('admin.pengumuman.create'); + Route::post('/admin/pengumuman', [AdminPageController::class, 'storePengumuman'])->name('admin.pengumuman.store'); + Route::get('/admin/jadwal', [AdminPageController::class, 'jadwal'])->name('admin.jadwal.index'); + Route::get('/admin/jadwal/kalender', [AdminPageController::class, 'kalender'])->name('admin.jadwal.kalender'); + Route::get('/admin/profile', [AdminPageController::class, 'profile'])->name('admin.profile'); + Route::get('/admin/users', [AdminPageController::class, 'users'])->name('admin.users'); + Route::get('/admin/pengaturan', [AdminPageController::class, 'pengaturan'])->name('admin.pengaturan'); +}); + +Route::middleware('legacy.role:dosen')->group(function () { + Route::get('/dashboard/dosen', DosenDashboardController::class)->name('dashboard.dosen'); + Route::get('/dosen/penawaran', [DosenPageController::class, 'penawaran'])->name('dosen.penawaran.index'); + Route::get('/dosen/penawaran/create', [DosenPageController::class, 'createPenawaran'])->name('dosen.penawaran.create'); + Route::post('/dosen/penawaran', [DosenPageController::class, 'storePenawaran'])->name('dosen.penawaran.store'); + Route::get('/dosen/penawaran/{id}/edit', [DosenPageController::class, 'editPenawaran'])->whereNumber('id')->name('dosen.penawaran.edit'); + Route::put('/dosen/penawaran/{id}', [DosenPageController::class, 'updatePenawaran'])->whereNumber('id')->name('dosen.penawaran.update'); + Route::delete('/dosen/penawaran/{id}', [DosenPageController::class, 'destroyPenawaran'])->whereNumber('id')->name('dosen.penawaran.destroy'); + Route::post('/dosen/penawaran/{id}/setuju', [DosenPageController::class, 'approvePenawaran'])->whereNumber('id')->name('dosen.penawaran.approve'); + Route::post('/dosen/penawaran/{id}/tolak', [DosenPageController::class, 'rejectPenawaran'])->whereNumber('id')->name('dosen.penawaran.reject'); + Route::get('/dosen/praoutline', [DosenPageController::class, 'daftarUsulan'])->name('dosen.praoutline.index'); + Route::get('/dosen/praoutline/review/{id}', [DosenPageController::class, 'reviewDetail'])->whereNumber('id')->name('dosen.praoutline.review'); + Route::get('/dosen/praoutline/review-saya', [DosenPageController::class, 'reviewSaya'])->name('dosen.praoutline.review-saya'); + Route::get('/dosen/praoutline/cari', [DosenPageController::class, 'cari'])->name('dosen.praoutline.cari'); + Route::get('/dosen/praoutline/bimbingan', [DosenPageController::class, 'bimbingan'])->name('dosen.praoutline.bimbingan'); + Route::get('/dosen/praoutline/statistik', [DosenPageController::class, 'statistik'])->name('dosen.praoutline.statistik'); + Route::get('/dosen/praoutline/pemberitahuan', [DosenPageController::class, 'pemberitahuan'])->name('dosen.praoutline.pemberitahuan'); + Route::get('/dosen/pengumuman', [DosenPageController::class, 'pengumuman'])->name('dosen.pengumuman.index'); + Route::get('/dosen/pengumuman/{id}', [DosenPageController::class, 'showPengumuman'])->whereNumber('id')->name('dosen.pengumuman.show'); + Route::get('/dosen/profile', [DosenPageController::class, 'profile'])->name('dosen.profile'); + Route::put('/dosen/profile', [DosenPageController::class, 'updateProfile'])->name('dosen.profile.update'); + Route::get('/dosen/early-warning', [DosenPageController::class, 'earlyWarning'])->name('dosen.early-warning'); + Route::get('/dosen/pra-lirs', [DosenPageController::class, 'praLirs'])->name('dosen.pra-lirs'); +}); diff --git a/rebuild/storage/app/.gitignore b/rebuild/storage/app/.gitignore new file mode 100644 index 0000000..fedb287 --- /dev/null +++ b/rebuild/storage/app/.gitignore @@ -0,0 +1,4 @@ +* +!private/ +!public/ +!.gitignore diff --git a/rebuild/storage/app/private/.gitignore b/rebuild/storage/app/private/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/rebuild/storage/app/private/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/rebuild/storage/app/public/.gitignore b/rebuild/storage/app/public/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/rebuild/storage/app/public/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/rebuild/storage/framework/.gitignore b/rebuild/storage/framework/.gitignore new file mode 100644 index 0000000..05c4471 --- /dev/null +++ b/rebuild/storage/framework/.gitignore @@ -0,0 +1,9 @@ +compiled.php +config.php +down +events.scanned.php +maintenance.php +routes.php +routes.scanned.php +schedule-* +services.json diff --git a/rebuild/storage/framework/sessions/.gitignore b/rebuild/storage/framework/sessions/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/rebuild/storage/framework/sessions/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/rebuild/storage/framework/testing/.gitignore b/rebuild/storage/framework/testing/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/rebuild/storage/framework/testing/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/rebuild/storage/framework/views/.gitignore b/rebuild/storage/framework/views/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/rebuild/storage/framework/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/rebuild/storage/logs/.gitignore b/rebuild/storage/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/rebuild/storage/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/rebuild/tests/Feature/ExampleTest.php b/rebuild/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..8364a84 --- /dev/null +++ b/rebuild/tests/Feature/ExampleTest.php @@ -0,0 +1,19 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/rebuild/tests/TestCase.php b/rebuild/tests/TestCase.php new file mode 100644 index 0000000..fe1ffc2 --- /dev/null +++ b/rebuild/tests/TestCase.php @@ -0,0 +1,10 @@ +assertTrue(true); + } +} diff --git a/rebuild/vite.config.js b/rebuild/vite.config.js new file mode 100644 index 0000000..f35b4e7 --- /dev/null +++ b/rebuild/vite.config.js @@ -0,0 +1,18 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + tailwindcss(), + ], + server: { + watch: { + ignored: ['**/storage/framework/views/**'], + }, + }, +});