commit a43fe060803c8aa2f0054ec9551c121ffc8d6f4d Author: Aryaaazrr <94944128+Aryaaazrr@users.noreply.github.com> Date: Sat May 10 23:23:14 2025 +0700 up diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8f0de65 --- /dev/null +++ b/.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 + +[docker-compose.yml] +indent_size = 4 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..d4c0d73 --- /dev/null +++ b/.env.example @@ -0,0 +1,90 @@ +APP_NAME=Laravel +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_TIMEZONE=UTC +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=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=spk_pm_dosbing +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 + +# PUSHER_APP_ID=1944527 +# PUSHER_APP_KEY=6a84a71e1b473120450c +# PUSHER_APP_SECRET=e65278939c6516ea0ef2 +# PUSHER_HOST= +# PUSHER_PORT=443 +# PUSHER_SCHEME="https" +# PUSHER_APP_CLUSTER="mt1" + +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_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=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}" + +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URI= + +REVERB_APP_ID= +REVERB_APP_KEY= +REVERB_APP_SECRET= +REVERB_HOST= +REVERB_PORT= +REVERB_SCHEME= + +VITE_REVERB_APP_KEY="${REVERB_APP_KEY}" +VITE_REVERB_HOST="${REVERB_HOST}" +VITE_REVERB_PORT="${REVERB_PORT}" +VITE_REVERB_SCHEME="${REVERB_SCHEME}" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/.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/.github/workflows/github.yml b/.github/workflows/github.yml new file mode 100644 index 0000000..5e49f2a --- /dev/null +++ b/.github/workflows/github.yml @@ -0,0 +1,42 @@ +name: SPK_PM_DOSBING +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] +jobs: + deploy: + name: deploy to staging + runs-on: ubuntu-latest + steps: + - uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e + with: + php-version: "8.2" + - uses: actions/checkout@v4 + - name: Copy .env + run: php -r "file_exists('.env') || copy('.env.example', '.env');" + - name: Install Dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --no-dev --optimize-autoloader --no-interaction --prefer-dist + - name: Generate key + run: php artisan key:generate + - name: Directory Permissions + run: chmod -R 777 storage bootstrap/cache + - name: Create Database + run: | + mkdir -p database + touch database/database.mysql + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20.x" + cache: "npm" + - name: Install Dependencies & Build + run: | + npm ci + npm run build + - name: 📂 Sync files + uses: SamKirkland/FTP-Deploy-Action@v4.3.5 + with: + server: ${{ secrets.ftp_servername }} + username: ${{ secrets.ftp_username }} + password: ${{ secrets.ftp_password }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..698ef51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/vendor +.env +.env.backup +.env.production +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.vscode +tailwind.config copy.js +composer.lock +.phpunit.result.cache diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a4c26b --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +

Laravel Logo

+ +

+Build Status +Total Downloads +Latest Stable Version +License +

+ +## About Laravel + +Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as: + +- [Simple, fast routing engine](https://laravel.com/docs/routing). +- [Powerful dependency injection container](https://laravel.com/docs/container). +- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage. +- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent). +- Database agnostic [schema migrations](https://laravel.com/docs/migrations). +- [Robust background job processing](https://laravel.com/docs/queues). +- [Real-time event broadcasting](https://laravel.com/docs/broadcasting). + +Laravel is accessible, powerful, and provides tools required for large, robust applications. + +## Learning Laravel + +Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. + +You may also try the [Laravel Bootcamp](https://bootcamp.laravel.com), where you will be guided through building a modern Laravel application from scratch. + +If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library. + +## Laravel Sponsors + +We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the [Laravel Partners program](https://partners.laravel.com). + +### Premium Partners + +- **[Vehikl](https://vehikl.com/)** +- **[Tighten Co.](https://tighten.co)** +- **[WebReinvent](https://webreinvent.com/)** +- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)** +- **[64 Robots](https://64robots.com)** +- **[Curotec](https://www.curotec.com/services/technologies/laravel/)** +- **[Cyber-Duck](https://cyber-duck.co.uk)** +- **[DevSquad](https://devsquad.com/hire-laravel-developers)** +- **[Jump24](https://jump24.co.uk)** +- **[Redberry](https://redberry.international/laravel/)** +- **[Active Logic](https://activelogic.com)** +- **[byte5](https://byte5.de)** +- **[OP.GG](https://op.gg)** + +## Contributing + +Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions). + +## Code of Conduct + +In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct). + +## Security Vulnerabilities + +If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed. + +## License + +The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT). diff --git a/app/Events/AlternatifUpdated.php b/app/Events/AlternatifUpdated.php new file mode 100644 index 0000000..04ec327 --- /dev/null +++ b/app/Events/AlternatifUpdated.php @@ -0,0 +1,38 @@ +alternatif = $alternatif; + } + + /** + * Get the channels the event should broadcast on. + * + * @return array + */ + public function broadcastOn(): array + { + return [ + new PrivateChannel('update-value'), + ]; + } +} diff --git a/app/Events/KuotaUpdated.php b/app/Events/KuotaUpdated.php new file mode 100644 index 0000000..bf0cf84 --- /dev/null +++ b/app/Events/KuotaUpdated.php @@ -0,0 +1,38 @@ +subkriteria = $subkriteria; + } + + /** + * Get the channels the event should broadcast on. + * + * @return array + */ + public function broadcastOn(): array + { + return [ + new Channel('kuota-channel'), + ]; + } +} \ No newline at end of file diff --git a/app/Events/SubmissionCreated.php b/app/Events/SubmissionCreated.php new file mode 100644 index 0000000..3e4e9ac --- /dev/null +++ b/app/Events/SubmissionCreated.php @@ -0,0 +1,40 @@ +submission = $submission; + } + + /** + * Get the channels the event should broadcast on. + * + * @return array + */ + public function broadcastOn(): array + { + return [ + new PrivateChannel('channel-name'), + ]; + } +} \ No newline at end of file diff --git a/app/Helpers/helpers.php b/app/Helpers/helpers.php new file mode 100644 index 0000000..b012555 --- /dev/null +++ b/app/Helpers/helpers.php @@ -0,0 +1,11 @@ +validated(); + + Alternatif::create($validated); + + return redirect()->route('alternatif.index')->with('success', 'Data Berhasil Ditambah'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menambahkan Data: ' . $e->getMessage()]); + } + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + $data['alternatif'] = Alternatif::find($id); + + return view('pages.alternatif.edit', $data); + } + + /** + * Update the specified resource in storage. + */ + public function update(AlternatifRequest $request, string $id) + { + try { + $validated = $request->validated(); + + Alternatif::find($id)->update($validated); + + return redirect()->route('alternatif.index')->with('success', 'Data Berhasil Diperbarui'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + + return back()->with(['error' => 'Gagal Mengubah Data: ' . $e->getMessage()]); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + try { + Alternatif::find($id)->delete(); + return redirect()->route('alternatif.index')->with('success', 'Data Berhasil Dihapus'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menghapus Data: ' . $e->getMessage()]); + } + } +} diff --git a/app/Http/Controllers/Api/V1/Alternatif/AlternatifController.php b/app/Http/Controllers/Api/V1/Alternatif/AlternatifController.php new file mode 100644 index 0000000..087c1a6 --- /dev/null +++ b/app/Http/Controllers/Api/V1/Alternatif/AlternatifController.php @@ -0,0 +1,65 @@ +limit ?? 10; + + $kriteria = Aspek::paginate($limit); + + return new AspekResource($kriteria); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V1/Kriteria/KriteriaController.php b/app/Http/Controllers/Api/V1/Kriteria/KriteriaController.php new file mode 100644 index 0000000..b1e97bb --- /dev/null +++ b/app/Http/Controllers/Api/V1/Kriteria/KriteriaController.php @@ -0,0 +1,71 @@ +limit ?? 10; + + $kriteria = Kriteria::where('id_aspek', '!=', 1)->with('subkriteria')->paginate($limit); + + return new KriteriaResource($kriteria); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/Api/V1/ProfileMatching/ProfileMatchingController.php b/app/Http/Controllers/Api/V1/ProfileMatching/ProfileMatchingController.php new file mode 100644 index 0000000..3487ad6 --- /dev/null +++ b/app/Http/Controllers/Api/V1/ProfileMatching/ProfileMatchingController.php @@ -0,0 +1,221 @@ +validated(); + + $data['judul'] = $request->input('judul'); + $data['deskripsi'] = $request->input('deskripsi'); + $selectedKriteria = $request->input('kriteria', []); + $selectedAlternatif = $request->input('alternatif', []); + + if (empty($selectedKriteria) || empty($selectedAlternatif)) { + return back()->withErrors('Kriteria dan alternatif harus dipilih.')->withInput(); + } + + Log::info('Selected Kriteria:', ['data' => $selectedKriteria]); + Log::info('Selected Alternatif:', ['data' => $selectedAlternatif]); + + $data['resultsGAP'] = $this->gapProfil($selectedAlternatif, $selectedKriteria); + $data['normalizedGAP'] = $this->normalizeGAP($data['resultsGAP']); + $data['factorValue'] = $this->factorValue($data['normalizedGAP']); + $data['totalValue'] = $this->totalValue($data['factorValue']); + $data['ranking'] = $this->rankAlternatif($data['totalValue']); + + $data['data_aspek'] = Aspek::all(); + $data['data_kriteria'] = Kriteria::all(); + + if (empty($data['resultsGAP']) || empty($data['ranking'])) { + return back()->withErrors('Terjadi kesalahan saat memproses data.')->withInput(); + } + + // dd($data); + return view('profile-matching.result', $data); + + } catch (\Exception $e) { + Log::error('Error in Profile Matching:', ['message' => $e->getMessage()]); + + return response()->json(['error' => 'Terjadi kesalahan saat memproses data: ' . $e->getMessage()], 500); + } +} + + private function gapProfil($alternatif, $kriteria) + { + $resultsGAP = []; + $gapValues = []; + + $dosenProfile = ProfileMethod::with('kriteria', 'subkriteria')->whereIn('id_alternatif', $alternatif)->get(); + + foreach ($dosenProfile as $dosen) { + + $dosenSubkriteria = $dosen->subkriteria; + $dosenKriteria = $dosen->kriteria; + + foreach ($kriteria as $key => $subkriteriaId) { + $idealValue = Subkriteria::with('kriteria')->find($subkriteriaId); + $idealKriteria = $idealValue->kriteria; + + if ($dosenKriteria->id_kriteria == $idealKriteria->id_kriteria) { + $gapValues[$idealValue->id_kriteria] = $dosenSubkriteria->nilai - $idealValue->nilai; + } + } + + $resultsGAP[$dosen->id_alternatif] = $gapValues; + } + Log::info('Ideal Value:', ['data' => $idealValue]); + Log::info('GAP Values:', ['dosenId' => $dosen->id_alternatif, 'gapValues' => $gapValues]); + + return $resultsGAP; + } + + private function normalizeGAP($resultsGAP) + { + $normalizedGAP = []; + $normalizedValues = []; + $conversionTable = [ + 0 => 5, + 1 => 4.5, + -1 => 4, + 2 => 3.5, + -2 => 3, + 3 => 2.5, + -3 => 2, + 4 => 1.5, + -4 => 1 + ]; + + foreach ($resultsGAP as $dosenId => $gapValues) { + + foreach ($gapValues as $key => $gap) { + $normalizedValues[$key] = $conversionTable[$gap] ?? 1; + } + + $normalizedGAP[$dosenId] = $normalizedValues; + } + + return $normalizedGAP; + } + + private function factorValue($normalizedGAP) { + $resultsFactor = []; + + foreach ($normalizedGAP as $dosenId => $gapValues) { + $dataAspek = Aspek::all(); + $aspek = []; + + foreach ($dataAspek as $aspekValue) { + $aspekId = $aspekValue->id_aspek; + + $coreValues = 0; + $secondaryValues = 0; + $coreCount = 0; + $secondaryCount = 0; + $factorValue = []; + + foreach ($gapValues as $kriteria => $values) { + $factor = Kriteria::find($kriteria); + + if ($factor->tipe == 'Core Factor') { + if ($aspekId == $factor->id_aspek) { + $coreValues += $values; + $coreCount++; + } + } else { + if ($aspekId == $factor->id_aspek) { + $secondaryValues += $values; + $secondaryCount++; + } + } + } + + if ($coreCount > 0) { + $factorValue['NCF'] = $coreValues / $coreCount; + } + if ($secondaryCount > 0) { + $factorValue['NSF'] = $secondaryValues / $secondaryCount; + } + + $aspek[$aspekId] = $factorValue; + } + $resultsFactor[$dosenId] = $aspek; + } + + return $resultsFactor; + } + + private function totalValue($factorValue) + { + $totalValues = []; + $aspek = []; + $totalValueFactor = []; + $bobotCF = 60; + $bobotSF = 40; + + foreach ($factorValue as $dosenId => $value) { + foreach ($value as $aspekId => $values) { + $value = []; + + foreach ($values as $tipeValue => $valueFactor) { + if ($tipeValue == 'NCF') { + $value[] = $valueFactor * $bobotCF / 100; + } else { + $value[] = $valueFactor * $bobotSF / 100; + } + } + $totalValueFactor = array_sum($value); + $aspek[$aspekId] = $totalValueFactor; + } + $totalValues[$dosenId] = $aspek; + } + + return $totalValues; + } + + private function rankAlternatif($totalValues) + { + $rankedResults = []; + + foreach ($totalValues as $dosenId => $totalValue) { + $ha = 0; + + foreach ($totalValue as $aspekId => $value) { + $bobotAspek = Aspek::where('id_aspek', $aspekId)->pluck('persentase')->first(); + if ($bobotAspek) { + $ha += $value * ($bobotAspek / 100); + } + } + $alternatif = Alternatif::where('id_alternatif', $dosenId)->first(); + + $rankedResults[] = [ + 'dosenId' => $alternatif->id_alternatif, + 'dosenName' => $alternatif->name, + 'hasilAkhir' => $ha, + ]; + } + + usort($rankedResults, function ($a, $b) { + return $b['hasilAkhir'] <=> $a['hasilAkhir']; + }); + + foreach ($rankedResults as $index => &$result) { + $result['rank'] = $index + 1; + } + + return $rankedResults; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V1/ProfileMethod/ProfileMethod.php b/app/Http/Controllers/Api/V1/ProfileMethod/ProfileMethod.php new file mode 100644 index 0000000..2ad4b20 --- /dev/null +++ b/app/Http/Controllers/Api/V1/ProfileMethod/ProfileMethod.php @@ -0,0 +1,70 @@ +limit ?? 10; + + $alternatif = ModelsProfileMethod::with('subkriteria')->paginate($limit); + + return new kriteriaResource($kriteria); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/V1/ProfileMethod/ProfileMethodController.php b/app/Http/Controllers/Api/V1/ProfileMethod/ProfileMethodController.php new file mode 100644 index 0000000..b3f36c8 --- /dev/null +++ b/app/Http/Controllers/Api/V1/ProfileMethod/ProfileMethodController.php @@ -0,0 +1,71 @@ +limit ?? 10; + + $profile = ProfileMethod::with('alternatif', 'kriteria', 'subkriteria')->paginate($limit); + + return new ProfileMethodResource($profile); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} \ No newline at end of file diff --git a/app/Http/Controllers/AspekController.php b/app/Http/Controllers/AspekController.php new file mode 100644 index 0000000..9ca56bc --- /dev/null +++ b/app/Http/Controllers/AspekController.php @@ -0,0 +1,113 @@ +validated(); + + $validated['persentase'] = FormatHelper::formatPersentase($validated['persentase']); + + Aspek::create($validated); + + return redirect()->route('aspek.index')->with('success', 'Data Berhasil Ditambah'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menambahkan Data: ' . $e->getMessage()]); + } + } + + + /** + * Display the specified resource. + */ + public function show(string $id) + { + $data['aspek'] = Aspek::find($id); + + return view('pages.aspek.show', $data); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + $data['aspek'] = Aspek::find($id); + + return view('pages.aspek.edit', $data); + } + + /** + * Update the specified resource in storage. + */ + public function update(AspekCreateRequest $request, string $id) + { + try { + $validated = $request->validated(); + + $validated['persentase'] = FormatHelper::formatPersentase($validated['persentase']); + + Aspek::find($id)->update($validated); + + return redirect()->route('aspek.index')->with('success', 'Data Berhasil Diperbarui'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + + return back()->with(['error' => 'Gagal Mengubah Data: ' . $e->getMessage()]); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + try { + Aspek::find($id)->delete(); + return redirect()->route('aspek.index')->with('success', 'Data Berhasil Dihapus'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menghapus Data: ' . $e->getMessage()]); + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php new file mode 100644 index 0000000..d50c8c8 --- /dev/null +++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php @@ -0,0 +1,47 @@ +authenticate(); + + $request->session()->regenerate(); + + return redirect()->intended(route('dashboard.index', absolute: false)); + } + + /** + * Destroy an authenticated session. + */ + public function destroy(Request $request): RedirectResponse + { + Auth::guard('web')->logout(); + + $request->session()->invalidate(); + + $request->session()->regenerateToken(); + + return redirect('/'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/ConfirmablePasswordController.php b/app/Http/Controllers/Auth/ConfirmablePasswordController.php new file mode 100644 index 0000000..5ee72d9 --- /dev/null +++ b/app/Http/Controllers/Auth/ConfirmablePasswordController.php @@ -0,0 +1,40 @@ +validate([ + 'email' => $request->user()->email, + 'password' => $request->password, + ])) { + throw ValidationException::withMessages([ + 'password' => __('auth.password'), + ]); + } + + $request->session()->put('auth.password_confirmed_at', time()); + + return redirect()->intended(route('dashboard.index', absolute: false)); + } +} diff --git a/app/Http/Controllers/Auth/EmailVerificationNotificationController.php b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php new file mode 100644 index 0000000..d06f675 --- /dev/null +++ b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php @@ -0,0 +1,24 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(route('dashboard.index', absolute: false)); + } + + $request->user()->sendEmailVerificationNotification(); + + return back()->with('status', 'verification-link-sent'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/EmailVerificationPromptController.php b/app/Http/Controllers/Auth/EmailVerificationPromptController.php new file mode 100644 index 0000000..afce27a --- /dev/null +++ b/app/Http/Controllers/Auth/EmailVerificationPromptController.php @@ -0,0 +1,21 @@ +user()->hasVerifiedEmail() + ? redirect()->intended(route('dashboard.index', absolute: false)) + : view('pages.auth.verify-email'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/NewPasswordController.php b/app/Http/Controllers/Auth/NewPasswordController.php new file mode 100644 index 0000000..0415962 --- /dev/null +++ b/app/Http/Controllers/Auth/NewPasswordController.php @@ -0,0 +1,61 @@ + $request]); + } + + /** + * Handle an incoming new password request. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function store(Request $request): RedirectResponse + { + $request->validate([ + 'token' => ['required'], + 'email' => ['required', 'email'], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + // Here we will attempt to reset the user's password. If it is successful we + // will update the password on an actual user model and persist it to the + // database. Otherwise we will parse the error and return the response. + $status = Password::reset( + $request->only('email', 'password', 'password_confirmation', 'token'), + function ($user) use ($request) { + $user->forceFill([ + 'password' => Hash::make($request->password), + 'remember_token' => Str::random(60), + ])->save(); + + event(new PasswordReset($user)); + } + ); + + // If the password was successfully reset, we will redirect the user back to + // the application's home authenticated view. If there is an error we can + // redirect them back to where they came from with their error message. + return $status == Password::PASSWORD_RESET + ? redirect()->route('login')->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php new file mode 100644 index 0000000..6916409 --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordController.php @@ -0,0 +1,29 @@ +validateWithBag('updatePassword', [ + 'current_password' => ['required', 'current_password'], + 'password' => ['required', Password::defaults(), 'confirmed'], + ]); + + $request->user()->update([ + 'password' => Hash::make($validated['password']), + ]); + + return back()->with('status', 'password-updated'); + } +} diff --git a/app/Http/Controllers/Auth/PasswordResetLinkController.php b/app/Http/Controllers/Auth/PasswordResetLinkController.php new file mode 100644 index 0000000..5e82056 --- /dev/null +++ b/app/Http/Controllers/Auth/PasswordResetLinkController.php @@ -0,0 +1,44 @@ +validate([ + 'email' => ['required', 'email'], + ]); + + // We will send the password reset link to this user. Once we have attempted + // to send the link, we will examine the response then see the message we + // need to show to the user. Finally, we'll send out a proper response. + $status = Password::sendResetLink( + $request->only('email') + ); + + return $status == Password::RESET_LINK_SENT + ? back()->with('status', __($status)) + : back()->withInput($request->only('email')) + ->withErrors(['email' => __($status)]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php new file mode 100644 index 0000000..d67a2e2 --- /dev/null +++ b/app/Http/Controllers/Auth/RegisteredUserController.php @@ -0,0 +1,52 @@ +validate([ + 'name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], + 'password' => ['required', 'confirmed', Rules\Password::defaults()], + ]); + + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => Hash::make($request->password), + ]); + + event(new Registered($user)); + + $user->assignRole('mahasiswa'); + + Auth::login($user); + + return redirect(route('dashboard.index', absolute: false)); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/ThirdParty/Google/GoogleController.php b/app/Http/Controllers/Auth/ThirdParty/Google/GoogleController.php new file mode 100644 index 0000000..75aa12e --- /dev/null +++ b/app/Http/Controllers/Auth/ThirdParty/Google/GoogleController.php @@ -0,0 +1,68 @@ +redirect(); + } + + public function handleGoogleCallback(): RedirectResponse + { + try { + $socialUser = Socialite::driver('google')->user(); + + $existingGoogleUser = User::where('google_id', $socialUser->id)->first(); + + if ($existingGoogleUser) { + Auth::login($existingGoogleUser); + } else { + $existingEmailUser = User::where('email', $socialUser->email)->first(); + + if ($existingEmailUser) { + $existingEmailUser->google_id = $socialUser->id; + $existingEmailUser->save(); + + Auth::login($existingEmailUser); + } else { + $username = $socialUser->nickname ?: strtolower(str_replace(' ', '', $socialUser->name)) . rand(100, 999); + + $newUser = User::create([ + 'username' => $username, + 'name' => $socialUser->name, + 'email' => $socialUser->email, + 'google_id' => $socialUser->id, + 'password' => Hash::make(Str::random(16)), + ]); + + event(new Registered($newUser)); + + $newUser->assignRole('mahasiswa'); + + Auth::login($newUser); + } + } + + return redirect('dashboard'); + } catch (Exception $e) { + Log::error('Google Login Error', ['message' => $e->getMessage()]); + + return redirect()->route('login')->with([ + 'error' => 'Login with Google failed. Please refresh and try again.', + ]); + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php new file mode 100644 index 0000000..3a6f6a8 --- /dev/null +++ b/app/Http/Controllers/Auth/VerifyEmailController.php @@ -0,0 +1,27 @@ +user()->hasVerifiedEmail()) { + return redirect()->intended(route('dashboard.index', absolute: false).'?verified=1'); + } + + if ($request->user()->markEmailAsVerified()) { + event(new Verified($request->user())); + } + + return redirect()->intended(route('dashboard.index', absolute: false).'?verified=1'); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php new file mode 100644 index 0000000..8677cd5 --- /dev/null +++ b/app/Http/Controllers/Controller.php @@ -0,0 +1,8 @@ +first() + ->users() + ->count(); + $data['jumlah_pengajuan'] = Submissions::count(); + + return view('pages.dashboard.index', $data); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} \ No newline at end of file diff --git a/app/Http/Controllers/KriteriaController.php b/app/Http/Controllers/KriteriaController.php new file mode 100644 index 0000000..92afe83 --- /dev/null +++ b/app/Http/Controllers/KriteriaController.php @@ -0,0 +1,105 @@ +validated(); + + Kriteria::create($validated); + + return redirect()->route('kriteria.index')->with('success', 'Data Berhasil Ditambah'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menambahkan Data: ' . $e->getMessage()]); + } + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + $data['kriteria'] = Kriteria::find($id); + $data['aspek'] = Aspek::all(); + $data['subkriteria'] = Subkriteria::with('kriteria')->where('id_kriteria', $id)->get(); + + return view('pages.kriteria.show', $data); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + $data['kriteria'] = Kriteria::find($id); + $data['aspek'] = Aspek::all(); + $data['subkriteria'] = Subkriteria::with('kriteria')->where('id_kriteria', $id)->get(); + + return view('pages.kriteria.edit', $data); + } + + /** + * Update the specified resource in storage. + */ + public function update(KriteriaRequest $request, string $id) + { + try { + $validated = $request->validated(); + + Kriteria::find($id)->update($validated); + + return redirect()->route('kriteria.index')->with('success', 'Data Berhasil Diperbarui'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + + return back()->with(['error' => 'Gagal Mengubah Data: ' . $e->getMessage()]); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + try { + Kriteria::find($id)->delete(); + return redirect()->route('kriteria.index')->with('success', 'Data Berhasil Dihapus'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menghapus Data: ' . $e->getMessage()]); + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/MahasiswaController.php b/app/Http/Controllers/MahasiswaController.php new file mode 100644 index 0000000..60c6758 --- /dev/null +++ b/app/Http/Controllers/MahasiswaController.php @@ -0,0 +1,67 @@ +get(); + + return view('pages.mahasiswa.index', $data); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} \ No newline at end of file diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php new file mode 100644 index 0000000..ca8e0d0 --- /dev/null +++ b/app/Http/Controllers/ProfileController.php @@ -0,0 +1,62 @@ + $request->user(), + 'angkatan' => Angkatan::all(), + ]); + } + + /** + * Update the user's profile information. + */ + public function update(ProfileUpdateRequest $request): RedirectResponse + { + $request->user()->fill($request->validated()); + + if ($request->user()->isDirty('email')) { + $request->user()->email_verified_at = null; + } + + $request->user()->save(); + + return Redirect::route('profile.edit')->with('status', 'profile-updated'); + } + + /** + * Delete the user's account. + */ + public function destroy(Request $request): RedirectResponse + { + $request->validateWithBag('userDeletion', [ + 'password' => ['required', 'current_password'], + ]); + + $user = $request->user(); + + Auth::logout(); + + $user->delete(); + + $request->session()->invalidate(); + $request->session()->regenerateToken(); + + return Redirect::to('/'); + } +} diff --git a/app/Http/Controllers/ProfileMatchingController.php b/app/Http/Controllers/ProfileMatchingController.php new file mode 100644 index 0000000..4f6bcfe --- /dev/null +++ b/app/Http/Controllers/ProfileMatchingController.php @@ -0,0 +1,287 @@ +all()); + $subkriteria1 = ProfileMethod::where('id_kriteria', 1) + ->pluck('id_subkriteria') + ->toArray(); + + // $subkriteria2 = ProfileMethod::where('id_kriteria', 2) + // ->pluck('id_subkriteria') + // ->toArray(); + + $countSubkriteria1 = array_count_values($subkriteria1); + // $countSubkriteria2 = array_count_values($subkriteria2); + + $mostKriteria1 = array_search(max($countSubkriteria1), $countSubkriteria1); + // $mostKriteria2 = array_search(max($countSubkriteria2), $countSubkriteria2); + + try { + $validated = $request->validated(); + + $selectKriteria = $request->input('kriteria', []); + $selectedAlternatif = $request->input('alternatif', []); + + $selectKriteria = array_merge([$mostKriteria1, 7], $selectKriteria); + $selectedKriteria = $selectKriteria; + + $selectedKriteria = array_map(function ($item) { + return (string) $item; + }, $selectedKriteria); + + if (empty($selectedKriteria) || empty($selectedAlternatif)) { + return back()->withErrors('Kriteria dan alternatif harus dipilih.')->withInput(); + } + + Log::info('Selected Kriteria:', ['data' => $selectedKriteria]); + Log::info('Selected Alternatif:', ['data' => $selectedAlternatif]); + + $data['selectedKriteria'] = $selectedKriteria; + $data['resultsGAP'] = $this->gapProfil($selectedAlternatif, $selectedKriteria); + $data['normalizedGAP'] = $this->normalizeGAP($data['resultsGAP']); + $data['factorValue'] = $this->factorValue($data['normalizedGAP']); + $data['totalValue'] = $this->totalValue($data['factorValue']); + $data['ranking'] = $this->rankAlternatif($data['totalValue']); + + $data['data_aspek'] = Aspek::all(); + $data['data_kriteria'] = Kriteria::all(); + + if (empty($data['resultsGAP']) || empty($data['ranking'])) { + return back()->withErrors('Terjadi kesalahan saat memproses data.')->withInput(); + } + + return view('profile-matching.result', $data); + + } catch (\Exception $e) { + Log::error('Error in Profile Matching:', ['message' => $e->getMessage()]); + + return response()->json(['error' => 'Terjadi kesalahan saat memproses data: ' . $e->getMessage()], 500); + } + } + + private function gapProfil($alternatif, $kriteria) + { + $resultsGAP = []; + $gapValues = []; + + $dosenProfile = ProfileMethod::with('kriteria', 'subkriteria')->whereIn('id_alternatif', $alternatif)->get(); + + foreach ($dosenProfile as $dosen) { + + $totalKeahlianUtama = ProfileMethod::with('kriteria') + ->where('id_alternatif', $dosen->id_alternatif) + ->whereHas('kriteria', function ($query) { + $query->where('kriteria_name', 'Keahlian Utama'); + })->count(); + + foreach ($kriteria as $subkriteriaId) { + $idealValue = Subkriteria::with('kriteria')->find($subkriteriaId); + $idealKriteria = $idealValue->kriteria; + + if ($dosen->kriteria->id_kriteria == $idealKriteria->id_kriteria) { + $dosenNilai = optional($dosen->subkriteria->nilai)->value; + $idealNilai = optional($idealValue->nilai)->value; + + $gap = $dosenNilai - $idealNilai; + + if ($idealKriteria->kriteria_name === 'Keahlian Utama') { + if ($totalKeahlianUtama > 1) { + if (abs($gap) <= 1) { + $gapValues[$idealKriteria->id_kriteria] = $gap; + } + } else { + $gapValues[$idealKriteria->id_kriteria] = $gap; + } + } else { + $gapValues[$idealKriteria->id_kriteria] = $gap; + } + } + } + + if (!empty($gapValues)) { + ksort($gapValues); + $resultsGAP[$dosen->id_alternatif] = $gapValues; + } + } + Log::info('Ideal Value:', ['data' => $idealValue]); + Log::info('GAP Values:', $resultsGAP); + + return $resultsGAP; + } + + private function normalizeGAP($resultsGAP) + { + $normalizedGAP = []; + $normalizedValues = []; + $conversionTable = [ + 0 => 5, + 1 => 4.5, + -1 => 4, + 2 => 3.5, + -2 => 3, + 3 => 2.5, + -3 => 2, + 4 => 1.5, + -4 => 1 + ]; + + foreach ($resultsGAP as $dosenId => $gapValues) { + + foreach ($gapValues as $key => $gap) { + $normalizedValues[$key] = $conversionTable[$gap] ?? 1; + } + + $normalizedGAP[$dosenId] = $normalizedValues; + } + + Log::info('Pemetaan GAP:', $normalizedGAP); + + return $normalizedGAP; + } + + private function factorValue($normalizedGAP) { + $resultsFactor = []; + + foreach ($normalizedGAP as $dosenId => $gapValues) { + $dataAspek = Aspek::all(); + $aspek = []; + + foreach ($dataAspek as $aspekValue) { + $aspekId = $aspekValue->id_aspek; + + $coreValues = 0; + $secondaryValues = 0; + $coreCount = 0; + $secondaryCount = 0; + $factorValue = []; + + foreach ($gapValues as $kriteria => $values) { + $factor = Kriteria::find($kriteria); + + if ($factor->tipe == 'Core Factor') { + if ($aspekId == $factor->id_aspek) { + $coreValues += $values; + $coreCount++; + } + } else { + if ($aspekId == $factor->id_aspek) { + $secondaryValues += $values; + $secondaryCount++; + } + } + } + + if ($coreCount > 0) { + $factorValue['NCF'] = $coreValues / $coreCount; + } + if ($secondaryCount > 0) { + $factorValue['NSF'] = $secondaryValues / $secondaryCount; + } + + $aspek[$aspekId] = $factorValue; + } + $resultsFactor[$dosenId] = $aspek; + } + + Log::info('NCSF:', $resultsFactor); + + return $resultsFactor; + } + + private function totalValue($factorValue) + { + $totalValues = []; + $aspek = []; + $totalValueFactor = []; + $bobotCF = 60; + $bobotSF = 40; + + foreach ($factorValue as $dosenId => $value) { + foreach ($value as $aspekId => $values) { + $value = []; + + foreach ($values as $tipeValue => $valueFactor) { + if ($tipeValue == 'NCF') { + $value[] = $valueFactor * $bobotCF / 100; + } else { + $value[] = $valueFactor * $bobotSF / 100; + } + } + $totalValueFactor = array_sum($value); + $aspek[$aspekId] = $totalValueFactor; + } + $totalValues[$dosenId] = $aspek; + } + + Log::info('Nilai Total:', $totalValues); + + return $totalValues; + } + + private function rankAlternatif($totalValues) + { + $rankedResults = []; + + foreach ($totalValues as $dosenId => $totalValue) { + $ha = 0; + + foreach ($totalValue as $aspekId => $value) { + $bobotAspek = Aspek::where('id_aspek', $aspekId)->pluck('persentase')->first(); + if ($bobotAspek) { + $ha += $value * ($bobotAspek / 100); + } + } + $alternatif = Alternatif::where('id_alternatif', $dosenId)->first(); + + $rankedResults[] = [ + 'dosenId' => $alternatif->id_alternatif, + 'dosenName' => $alternatif->name, + 'hasilAkhir' => $ha, + ]; + } + + usort($rankedResults, function ($a, $b) { + return $b['hasilAkhir'] <=> $a['hasilAkhir']; + }); + + foreach ($rankedResults as $index => &$result) { + $result['rank'] = $index + 1; + } + + Log::info('Ranking:', $rankedResults); + + return $rankedResults; + } + + public function submit(SubmissionRequest $request) + { + try { + $validated = $request->validated(); + + Submissions::create($validated); + + return redirect()->route('results.data')->with('success', 'Pengajuan Judul Berhasil'); + // return view('profile-matching.result'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Pengajuan Judul Gagal: ' . $e->getMessage()]); + } + } + +} diff --git a/app/Http/Controllers/ProfileMethodController.php b/app/Http/Controllers/ProfileMethodController.php new file mode 100644 index 0000000..9c5fa08 --- /dev/null +++ b/app/Http/Controllers/ProfileMethodController.php @@ -0,0 +1,167 @@ +get(); + + return view('pages.profile-method.index', $data); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + $data['alternatif'] = Alternatif::with('profile_method')->whereDoesntHave('profile_method')->get(); + $data['kriteria'] = Kriteria::all(); + $data['subkriteria'] = Subkriteria::all(); + + return view('pages.profile-method.create', $data); + } + + /** + * Store a newly created resource in storage. + */ + public function store(ProfileMethodRequest $request) + { + try { + foreach ($request->kriteria as $id_kriteria => $id_subkriteria) { + if (is_array($id_subkriteria)) { + foreach ($id_subkriteria as $subId) { + if ($subId) { + ProfileMethod::updateOrCreate( + [ + 'id_alternatif' => $request->id_alternatif, + 'id_kriteria' => $id_kriteria, + 'id_subkriteria' => $subId, + ], + [] + ); + } + } + } else { + if ($id_subkriteria) { + ProfileMethod::updateOrCreate( + [ + 'id_alternatif' => $request->id_alternatif, + 'id_kriteria' => $id_kriteria, + ], + [ + 'id_subkriteria' => $id_subkriteria, + ] + ); + } + } + } + + return redirect()->route('method-profile.index')->with('success', 'Data Berhasil Ditambah'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menambahkan Data: ' . $e->getMessage()]); + } + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + $data['profile_method'] = ProfileMethod::with('alternatif', 'kriteria', 'subkriteria') + ->where('id_alternatif', $id) + ->get(); + $data['alternatif'] = Alternatif::findOrFail($id); + $data['kriteria'] = Kriteria::all(); + + return view('pages.profile-method.edit', $data); + } + + /** + * Update the specified resource in storage. + */ + public function update(ProfileMethodRequest $request, string $id) + { + try { + foreach ($request->kriteria as $id_kriteria => $id_subkriteria) { + if (is_array($id_subkriteria)) { + // Hapus semua yang tidak termasuk dalam pilihan baru + ProfileMethod::where('id_alternatif', $request->id_alternatif) + ->where('id_kriteria', $id_kriteria) + ->whereNotIn('id_subkriteria', $id_subkriteria) + ->delete(); + + foreach ($id_subkriteria as $subId) { + if ($subId) { + ProfileMethod::updateOrCreate( + [ + 'id_alternatif' => $request->id_alternatif, + 'id_kriteria' => $id_kriteria, + 'id_subkriteria' => $subId, + ], + [] + ); + } + } + } else { + if ($id_subkriteria) { + // Update untuk kriteria dengan 1 nilai saja + ProfileMethod::updateOrCreate( + [ + 'id_alternatif' => $request->id_alternatif, + 'id_kriteria' => $id_kriteria, + ], + [ + 'id_subkriteria' => $id_subkriteria, + ] + ); + } + } + } + + return redirect()->route('method-profile.index')->with('success', 'Data Berhasil Diperbarui'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Memperbarui Data: ' . $e->getMessage()]); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + try { + $profile_method = ProfileMethod::where('id_alternatif', $id); + + $profile_method->delete(); + + return redirect()->route('method-profile.index')->with('success', 'Data Berhasil Dihapus'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menghapus Data: ' . $e->getMessage()]); + } + } +} diff --git a/app/Http/Controllers/SubkriteriaController.php b/app/Http/Controllers/SubkriteriaController.php new file mode 100644 index 0000000..c7a63f5 --- /dev/null +++ b/app/Http/Controllers/SubkriteriaController.php @@ -0,0 +1,114 @@ +id_kriteria)->count(); + + if ($count >= 5) { + return redirect()->route('kriteria.edit', $kriteria)->withErrors(['Kriteria ' . $kriteria->kriteria_name .' sudah memiliki subkriteria maksimal 5']); + } + + $data['kriteria'] = $kriteria; + $data['nilai'] = Nilai::all(); + + return view('pages.subkriteria.create', $data); + } + + /** + * Store a newly created resource in storage. + */ + public function store(SubkriteriaRequest $request, $kriteria) + { + try { + $validated = $request->validated(); + + Subkriteria::create($validated); + + return redirect()->route('kriteria.edit', $kriteria)->with('success', 'Data Berhasil Ditambah'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menambahkan Data: ' . $e->getMessage()]); + } + } + + /** + * Display the specified resource. + */ + public function show(Kriteria $kriteria, Subkriteria $subkriteria) + { + $data['subkriteria'] = $subkriteria; + $data['kriteria'] = $kriteria; + $data['nilai'] = Nilai::all(); + + return view('pages.subkriteria.show', $data); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(Kriteria $kriteria, Subkriteria $subkriteria) + { + $data['subkriteria'] = $subkriteria; + $data['kriteria'] = $kriteria; + $data['nilai'] = Nilai::all(); + + return view('pages.subkriteria.edit', $data); + } + + /** + * Update the specified resource in storage. + */ + public function update(SubkriteriaRequest $request, $kriteria, $subkriteria) + { + try { + $validated = $request->validated(); + + Subkriteria::find($subkriteria)->update($validated); + + return redirect()->route('kriteria.edit', $kriteria)->with('success', 'Data Berhasil Diperbarui'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + + return back()->with(['error' => 'Gagal Mengubah Data: ' . $e->getMessage()]); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy($kriteria, $subkriteria) + { + try { + Subkriteria::find($subkriteria)->delete(); + + return redirect()->route('kriteria.edit', $kriteria)->with('success', 'Data Berhasil Dihapus'); + } catch (\Exception $e) { + Log::error($e->getMessage()); + return back()->with(['error' => 'Gagal Menghapus Data: ' . $e->getMessage()]); + } + } +} diff --git a/app/Http/Controllers/SubmissionsController.php b/app/Http/Controllers/SubmissionsController.php new file mode 100644 index 0000000..7fc96cc --- /dev/null +++ b/app/Http/Controllers/SubmissionsController.php @@ -0,0 +1,127 @@ +get(); + $data['submission_detail'] = SubmissionDetail::with('alternatif')->get(); + $data['profile'] = ProfileMethod::with(['alternatif', 'kriteria', 'subkriteria'])->get(); + $data['kriteria'] = Kriteria::with('subkriteria')->get(); + $data['izin'] = IzinPemilihan::first(); + + return view('pages.submissions.index', $data); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + $data['alternatif'] = Alternatif::all(); + $data['izin'] = IzinPemilihan::first(); + + if (Auth::check()) { + $user = Auth::user(); + + if ($user->hasRole(User::ROLE_MAHASISWA)) { + if (empty($user->nim) || empty($user->id_angkatan)) { + Log::error('Submission gagal: Profil tidak lengkap', [ + 'id' => $user->id, + 'nim' => $user->nim, + 'id_angkatan' => $user->id_angkatan, + ]); + + return redirect()->route('profile.edit')->withErrors(['error' => 'Gagal mengakses pengajuan: Silahkan lengkapi profil anda!']); + } + } + } + + return view('pages.submissions.create', $data); + } + + /** + * Store a newly created resource in storage. + */ + public function store(SubmissionRequest $request) + { + DB::beginTransaction(); + + try { + $validatedSubmission = $request->validated(); + + $submission = Submissions::create([ + 'id' => $validatedSubmission['id'], + 'judul' => $validatedSubmission['judul'], + 'deskripsi' => $validatedSubmission['deskripsi'], + 'prodi' => $validatedSubmission['prodi'], + ]); + + SubmissionDetail::create([ + 'id_submission' => $submission->id_submission, + 'id_alternatif' => $validatedSubmission['id_alternatif'], + ]); + + DB::commit(); + + return redirect()->route('submissions.create')->with('success', 'Pengajuan Berhasil'); + } catch (\Exception $e) { + DB::rollBack(); + Log::error($e->getMessage()); + + return back()->withErrors(['error' => 'Pengajuan Gagal: Terjadi kesalahan!']); + } + } + + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/SystemController.php b/app/Http/Controllers/SystemController.php new file mode 100644 index 0000000..e611c83 --- /dev/null +++ b/app/Http/Controllers/SystemController.php @@ -0,0 +1,85 @@ +validate([ + 'izin' => 'required|in:on,off' + ]); + + $izin = IzinPemilihan::first(); + $izin->izin = $request->izin; + $izin->save(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Izin berhasil diperbarui!', + 'izin' => $izin->izin + ], 200); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} \ No newline at end of file diff --git a/app/Http/Requests/Alternatif/AlternatifRequest.php b/app/Http/Requests/Alternatif/AlternatifRequest.php new file mode 100644 index 0000000..a22da72 --- /dev/null +++ b/app/Http/Requests/Alternatif/AlternatifRequest.php @@ -0,0 +1,46 @@ +|string> + */ + public function rules(): array + { + return [ + 'nip' => ['required', 'string', 'max:225'], + 'name' => ['required', 'string', 'max:255'], + ]; + } + + /** + * Get the custom error messages for validation errors. + * + * @return array + */ + public function messages(): array + { + return [ + 'nip.required' => 'NIP harus diisi.', + 'nip.string' => 'NIP harus berupa teks.', + 'nip.max' => 'NIP tidak boleh lebih dari 255 karakter.', + 'name.required' => 'Nama Kriteria harus diisi.', + 'name.string' => 'Nama Kriteria harus berupa teks.', + 'name.max' => 'Nama Kriteria tidak boleh lebih dari 255 karakter.', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Aspek/AspekCreateRequest.php b/app/Http/Requests/Aspek/AspekCreateRequest.php new file mode 100644 index 0000000..480f9aa --- /dev/null +++ b/app/Http/Requests/Aspek/AspekCreateRequest.php @@ -0,0 +1,47 @@ +|string> + */ + public function rules(): array + { + return [ + 'aspek_name' => ['required', 'string', 'max:225'], + 'persentase' => ['required', 'string', 'min:0', 'max:100'], + 'keterangan' => ['nullable', 'string', 'max:500'], + ]; + } + + public function messages(): array + { + return [ + 'aspek_name.required' => 'Nama aspek wajib diisi.', + 'aspek_name.string' => 'Nama aspek harus berupa teks.', + 'aspek_name.max' => 'Nama aspek maksimal 225 karakter.', + + 'persentase.required' => 'Persentase wajib diisi.', + 'persentase.numeric' => 'Persentase harus berupa angka.', + 'persentase.min' => 'Persentase tidak boleh kurang dari 0.', + 'persentase.max' => 'Persentase tidak boleh lebih dari 100.', + + 'keterangan.string' => 'Keterangan harus berupa teks.', + 'keterangan.max' => 'Keterangan maksimal 500 karakter.', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php new file mode 100644 index 0000000..2574642 --- /dev/null +++ b/app/Http/Requests/Auth/LoginRequest.php @@ -0,0 +1,85 @@ +|string> + */ + public function rules(): array + { + return [ + 'email' => ['required', 'string', 'email'], + 'password' => ['required', 'string'], + ]; + } + + /** + * Attempt to authenticate the request's credentials. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function authenticate(): void + { + $this->ensureIsNotRateLimited(); + + if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { + RateLimiter::hit($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.failed'), + ]); + } + + RateLimiter::clear($this->throttleKey()); + } + + /** + * Ensure the login request is not rate limited. + * + * @throws \Illuminate\Validation\ValidationException + */ + public function ensureIsNotRateLimited(): void + { + if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { + return; + } + + event(new Lockout($this)); + + $seconds = RateLimiter::availableIn($this->throttleKey()); + + throw ValidationException::withMessages([ + 'email' => trans('auth.throttle', [ + 'seconds' => $seconds, + 'minutes' => ceil($seconds / 60), + ]), + ]); + } + + /** + * Get the rate limiting throttle key for the request. + */ + public function throttleKey(): string + { + return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip()); + } +} diff --git a/app/Http/Requests/Kriteria/KriteriaRequest.php b/app/Http/Requests/Kriteria/KriteriaRequest.php new file mode 100644 index 0000000..fef6578 --- /dev/null +++ b/app/Http/Requests/Kriteria/KriteriaRequest.php @@ -0,0 +1,53 @@ +|string> + */ + public function rules(): array + { + return [ + 'kriteria_name' => ['required', 'string', 'max:255'], + 'tipe' => ['required', 'string', 'in:Core Factor,Secondary Factor'], + 'id_aspek' => ['required'], + 'keterangan' => ['nullable', 'string', 'max:500'], + ]; + } + + /** + * Get the custom error messages for validation errors. + * + * @return array + */ + public function messages(): array + { + return [ + 'kriteria_name.required' => 'Nama Kriteria harus diisi.', + 'kriteria_name.string' => 'Nama Kriteria harus berupa teks.', + 'kriteria_name.max' => 'Nama Kriteria tidak boleh lebih dari 255 karakter.', + 'id_aspek.required' => 'Aspek harus dipilih.', + 'tipe.required' => 'Tipe harus diisi.', + 'tipe.string' => 'Tipe harus berupa teks.', + 'tipe.in' => 'Tipe harus salah satu dari Core Factor atau Secondary Factor.', + 'keterangan.string' => 'Keterangan harus berupa teks.', + 'keterangan.max' => 'Keterangan tidak boleh lebih dari 500 karakter.', + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/ProfileMatching/ProfileMatchingRequest.php b/app/Http/Requests/ProfileMatching/ProfileMatchingRequest.php new file mode 100644 index 0000000..6817994 --- /dev/null +++ b/app/Http/Requests/ProfileMatching/ProfileMatchingRequest.php @@ -0,0 +1,67 @@ +|string> + */ + public function rules(): array + { + // $rules = [ + // 'judul' => ['required', 'max:255'], + // 'deskripsi' => ['required', 'max:255'], + // ]; + + $rules['kriteria'] = ['required', 'array']; + foreach (range(0, 3) as $index) { + $rules["kriteria.$index"] = ['required', 'exists:subkriteria,id_subkriteria']; + } + + $rules['alternatif'] = ['required', 'array', 'min:1', 'max:3']; + foreach (range(0, 2) as $index) { + $rules["alternatif.$index"] = ['nullable', 'exists:alternatif,id_alternatif']; + } + + return $rules; + } + + /** + * Get the custom error messages for validation errors. + * + * @return array + */ + public function messages(): array + { + $messages = [ + 'judul.required' => 'Judul harus diisi.', + 'judul.max' => 'Judul tidak boleh lebih dari 255 karakter.', + 'deskripsi.required' => 'Deskripsi harus diisi.', + + 'kriteria.required' => 'Semua kriteria harus diisi.', + 'kriteria.array' => 'Format kriteria tidak valid.', + 'kriteria.*.exists' => 'Kriteria yang dipilih tidak valid.', + + 'alternatif.required' => 'Setidaknya satu alternatif harus dipilih.', + 'alternatif.array' => 'Format alternatif tidak valid.', + 'alternatif.min' => 'Setidaknya satu alternatif harus dipilih.', + 'alternatif.max' => 'Tidak boleh lebih dari tiga alternatif.', + 'alternatif.*.exists' => 'Alternatif yang dipilih tidak valid.', + ]; + + return $messages; + } +} diff --git a/app/Http/Requests/ProfileMethod/ProfileMethodRequest.php b/app/Http/Requests/ProfileMethod/ProfileMethodRequest.php new file mode 100644 index 0000000..629b88d --- /dev/null +++ b/app/Http/Requests/ProfileMethod/ProfileMethodRequest.php @@ -0,0 +1,43 @@ +|string> + */ + public function rules(): array + { + return [ + 'id_alternatif' => 'required|exists:alternatif,id_alternatif', + 'kriteria' => 'required|array', + 'kriteria.*' => 'required|exists:subkriteria,id_subkriteria', + ]; + } + + /** + * Pesan error khusus untuk validasi. + */ + public function messages(): array + { + return [ + 'id_alternatif.required' => 'Nama alternatif wajib diisi.', + 'id_alternatif.exists' => 'Alternatif yang dipilih tidak valid.', + 'kriteria.required' => 'Nilai kriteria harus diisi.', + 'kriteria.*.exists' => 'Subkriteria yang dipilih tidak valid.', + ]; + } +} diff --git a/app/Http/Requests/ProfileUpdateRequest.php b/app/Http/Requests/ProfileUpdateRequest.php new file mode 100644 index 0000000..32dc4e0 --- /dev/null +++ b/app/Http/Requests/ProfileUpdateRequest.php @@ -0,0 +1,34 @@ +|string> + */ + public function rules(): array + { + return [ + 'nim' => ['required', 'string'], + 'name' => ['required', 'string', 'max:255'], + 'email' => [ + 'required', + 'string', + 'lowercase', + 'email', + 'max:255', + Rule::unique(User::class)->ignore($this->user()->id), + ], + 'id_angkatan' => [ + 'required' + ] + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Subkriteria/SubkriteriaRequest.php b/app/Http/Requests/Subkriteria/SubkriteriaRequest.php new file mode 100644 index 0000000..26a3c96 --- /dev/null +++ b/app/Http/Requests/Subkriteria/SubkriteriaRequest.php @@ -0,0 +1,30 @@ +|string> + */ + public function rules(): array + { + return [ + 'id_kriteria' => ['required'], + 'subkriteria_name' => ['required', 'string', 'max:255'], + // 'id_nilai' => ['required'], + ]; + } +} diff --git a/app/Http/Requests/Submission/SubmissionDetailRequest.php b/app/Http/Requests/Submission/SubmissionDetailRequest.php new file mode 100644 index 0000000..c6a502b --- /dev/null +++ b/app/Http/Requests/Submission/SubmissionDetailRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'id_submission' => ['required'], + 'id_alternatif' => ['required'], + ]; + } +} \ No newline at end of file diff --git a/app/Http/Requests/Submission/SubmissionRequest.php b/app/Http/Requests/Submission/SubmissionRequest.php new file mode 100644 index 0000000..8619b71 --- /dev/null +++ b/app/Http/Requests/Submission/SubmissionRequest.php @@ -0,0 +1,32 @@ +|string> + */ + public function rules(): array + { + return [ + 'id' => ['required'], + 'prodi' => ['required', 'string', 'max:255'], + 'judul' => ['required', 'string', 'max:255'], + 'deskripsi' => ['required', 'string', 'max:255'], + 'id_alternatif' => ['required'], + ]; + } +} \ No newline at end of file diff --git a/app/Http/Resources/AspekResource.php b/app/Http/Resources/AspekResource.php new file mode 100644 index 0000000..105c864 --- /dev/null +++ b/app/Http/Resources/AspekResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'aspek' => $this->collection, + 'pagination' => [ + 'total' => $this->total(), + 'count' => $this->count(), + 'per_page' => $this->perPage(), + 'current_page' => $this->currentPage(), + 'total_pages' => $this->lastPage() + ] + ]; + } +} \ No newline at end of file diff --git a/app/Http/Resources/KriteriaResource.php b/app/Http/Resources/KriteriaResource.php new file mode 100644 index 0000000..1659c4d --- /dev/null +++ b/app/Http/Resources/KriteriaResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'data' => $this->collection, + 'pagination' => [ + 'total' => $this->total(), + 'count' => $this->count(), + 'per_page' => $this->perPage(), + 'current_page' => $this->currentPage(), + 'total_pages' => $this->lastPage() + ] + ]; + } +} \ No newline at end of file diff --git a/app/Http/Resources/ProfileMethodResource.php b/app/Http/Resources/ProfileMethodResource.php new file mode 100644 index 0000000..c3e283f --- /dev/null +++ b/app/Http/Resources/ProfileMethodResource.php @@ -0,0 +1,28 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'profile' => $this->collection, + 'pagination' => [ + 'total' => $this->total(), + 'count' => $this->count(), + 'per_page' => $this->perPage(), + 'current_page' => $this->currentPage(), + 'total_pages' => $this->lastPage() + ] + ]; + } +} \ No newline at end of file diff --git a/app/Jobs/ProcessSubmission.php b/app/Jobs/ProcessSubmission.php new file mode 100644 index 0000000..3f22c99 --- /dev/null +++ b/app/Jobs/ProcessSubmission.php @@ -0,0 +1,90 @@ +submissions = $submission; + } + + public function handle() + { + try { + Log::info("Menjalankan ProcessSubmission untuk submission: ", ['id_submission' => $this->submissions->id_submission]); + + $user = User::find($this->submissions->id); + + if (!$user || !$user->id_angkatan) { + Log::warning('Submission dibuat user tanpa data angkatan.', ['id_submission' => $this->submissions->id_submission]); + return; + } + + // Tunggu hingga SubmissionDetail tersedia + $maxRetries = 5; + $retryInterval = 2; // Coba setiap 2 detik + + for ($i = 0; $i < $maxRetries; $i++) { + $submissionDetails = SubmissionDetail::where('id_submission', $this->submissions->id_submission)->get(); + + if (!$submissionDetails->isEmpty()) { + break; + } + + Log::info("Menunggu SubmissionDetail dalam job untuk id_submission: " . $this->submissions->id_submission); + sleep($retryInterval); + } + + if ($submissionDetails->isEmpty()) { + Log::warning('SubmissionDetail masih tidak ditemukan setelah menunggu di job.', ['id_submission' => $this->submissions->id_submission]); + return; + } + + $id_angkatan = $user->id_angkatan; + + $alternatifCounts = SubmissionDetail::join('submission', 'submission.id_submission', '=', 'submission_detail.id_submission') + ->join('users', 'users.id', '=', 'submission.id_user') + ->where('users.angkatan', $id_angkatan) + ->selectRaw('id_alternatif, COUNT(*) as total_permintaan') + ->groupBy('id_alternatif') + ->pluck('total_permintaan', 'id_alternatif'); + + foreach ($submissionDetails as $detail) { + $id_alternatif = $detail->id_alternatif; + $total_permintaan = $alternatifCounts[$id_alternatif] ?? 0; + + $kuotaMax = ProfileMethod::where('id_alternatif', $id_alternatif)->value('id_nilai') ?? 0; + + $rasio = ($total_permintaan > 0 && $kuotaMax > 0) ? ($total_permintaan / $kuotaMax) * 100 : 0; + + Log::info('Perhitungan kuota untuk alternatif', [ + 'id_alternatif' => $id_alternatif, + 'total_permintaan' => $total_permintaan, + 'kuota_maksimum' => $kuotaMax, + 'rasio_kuota' => $rasio . '%' + ]); + } + + } catch (\Exception $e) { + Log::error('Terjadi kesalahan saat menghitung kuota dalam job:', [ + 'id_submission' => $this->submissions->id_submission, + 'error' => $e->getMessage() + ]); + } + } + +} diff --git a/app/Models/Alternatif.php b/app/Models/Alternatif.php new file mode 100644 index 0000000..2134cf8 --- /dev/null +++ b/app/Models/Alternatif.php @@ -0,0 +1,51 @@ +nip)) { + $model->nip = self::formatNip($model->nip); + } + }); + } + + public static function formatNip($nip) + { + $nip = preg_replace('/\s+/', '', $nip); + + return substr($nip, 0, 8) . ' ' . + substr($nip, 8, 6) . ' ' . + substr($nip, 14, 1) . ' ' . + substr($nip, 15); + } + + public function profile_method() + { + return $this->hasMany(ProfileMethod::class, 'id_alternatif'); + } + + public function submission_detail() + { + return $this->hasMany(SubmissionDetail::class, 'id_alternatif'); + } +} \ No newline at end of file diff --git a/app/Models/Angkatan.php b/app/Models/Angkatan.php new file mode 100644 index 0000000..0b96887 --- /dev/null +++ b/app/Models/Angkatan.php @@ -0,0 +1,22 @@ +hasMany(User::class, 'id_angkatan'); + } +} \ No newline at end of file diff --git a/app/Models/Aspek.php b/app/Models/Aspek.php new file mode 100644 index 0000000..dcb532a --- /dev/null +++ b/app/Models/Aspek.php @@ -0,0 +1,37 @@ +first(); + $lastNumber = $latestAspek ? intval(substr($latestAspek->kode_aspek, 1)) : 0; + $newNumber = $lastNumber + 1; + + $aspek->kode_aspek = 'A' . str_pad($newNumber, 3, '0', STR_PAD_LEFT); + }); + } + + public function kriteria() + { + return $this->hasMany(Kriteria::class, 'id_aspek'); + } +} \ No newline at end of file diff --git a/app/Models/IzinPemilihan.php b/app/Models/IzinPemilihan.php new file mode 100644 index 0000000..061ab3c --- /dev/null +++ b/app/Models/IzinPemilihan.php @@ -0,0 +1,16 @@ +first(); + $lastNumber = $latestKriteria ? intval(substr($latestKriteria->kode_kriteria, 1)) : 0; + $newNumber = $lastNumber + 1; + + $kriteria->kode_kriteria = 'K' . str_pad($newNumber, 3, '0', STR_PAD_LEFT); + }); + } + + public static function getTipeOptions() + { + return [ + self::TIPE_CORE => 'Core Factor', + self::TIPE_SECONDARY => 'Secondary Factor', + ]; + } + + public function aspek() + { + return $this->belongsTo(Aspek::class, 'id_aspek'); + } + + public function subkriteria() + { + return $this->hasMany(Subkriteria::class, 'id_kriteria'); + } + + public function profile_method() + { + return $this->hasMany(ProfileMethod::class, 'id_kriteria'); + } +} \ No newline at end of file diff --git a/app/Models/Nilai.php b/app/Models/Nilai.php new file mode 100644 index 0000000..ce4b867 --- /dev/null +++ b/app/Models/Nilai.php @@ -0,0 +1,21 @@ +hasMany(Subkriteria::class, 'id_nilai'); + } +} \ No newline at end of file diff --git a/app/Models/ProfileMethod.php b/app/Models/ProfileMethod.php new file mode 100644 index 0000000..6730500 --- /dev/null +++ b/app/Models/ProfileMethod.php @@ -0,0 +1,33 @@ +belongsTo(Alternatif::class, 'id_alternatif'); + } + + public function kriteria() + { + return $this->belongsTo(Kriteria::class, 'id_kriteria'); + } + + public function subkriteria() + { + return $this->belongsTo(Subkriteria::class, 'id_subkriteria'); + } +} \ No newline at end of file diff --git a/app/Models/Setting.php b/app/Models/Setting.php new file mode 100644 index 0000000..f1124ee --- /dev/null +++ b/app/Models/Setting.php @@ -0,0 +1,56 @@ +where('key', $key); + } + + public function scopeLocale($query, $lang) + { + return $query->whereLocale($lang); + } + + public function getJsonValueAttribute($value) + { + return json_decode($value); + } + + public function getAdditionalJsonValueAttribute($value) + { + return json_decode($value); + } +} \ No newline at end of file diff --git a/app/Models/Subkriteria.php b/app/Models/Subkriteria.php new file mode 100644 index 0000000..d7d9d31 --- /dev/null +++ b/app/Models/Subkriteria.php @@ -0,0 +1,34 @@ +belongsTo(Kriteria::class, 'id_kriteria'); + } + + public function nilai() + { + return $this->belongsTo(nilai::class, 'id_nilai'); + } + + public function profile_method() + { + return $this->hasMany(ProfileMethod::class, 'id_subkriteria'); + } +} \ No newline at end of file diff --git a/app/Models/SubmissionDetail.php b/app/Models/SubmissionDetail.php new file mode 100644 index 0000000..e65071a --- /dev/null +++ b/app/Models/SubmissionDetail.php @@ -0,0 +1,33 @@ +belongsTo(Submissions::class, 'id_submission'); + } + + public function alternatif() + { + return $this->belongsTo(Alternatif::class, 'id_alternatif'); + } +} \ No newline at end of file diff --git a/app/Models/Submissions.php b/app/Models/Submissions.php new file mode 100644 index 0000000..da34e98 --- /dev/null +++ b/app/Models/Submissions.php @@ -0,0 +1,41 @@ +belongsTo(User::class, 'id'); + } + + public function submission_detail() + { + return $this->hasMany(SubmissionDetail::class, 'id_submission'); + } + +} \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php new file mode 100644 index 0000000..2c9b722 --- /dev/null +++ b/app/Models/User.php @@ -0,0 +1,66 @@ + */ + use HasFactory, Notifiable, HasRoles; + + public const ROLE_ADMIN = 'admin'; + public const ROLE_MAHASISWA = 'mahasiswa'; + + /** + * The attributes that are mass assignable. + * + * @var array + */ + protected $fillable = [ + 'nim', + 'name', + 'email', + 'password', + 'id_angkatan', + ]; + + /** + * The attributes that should be hidden for serialization. + * + * @var array + */ + protected $hidden = [ + 'password', + 'remember_token', + ]; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'email_verified_at' => 'datetime', + 'password' => 'hashed', + ]; + } + + public function angkatan() + { + return $this->belongsTo(Angkatan::class, 'id_angkatan'); + } + + public function submission() + { + return $this->hasMany(Submissions::class, 'id'); + } +} \ No newline at end of file diff --git a/app/Observers/SubmissionsDetailObserver.php b/app/Observers/SubmissionsDetailObserver.php new file mode 100644 index 0000000..d875f05 --- /dev/null +++ b/app/Observers/SubmissionsDetailObserver.php @@ -0,0 +1,125 @@ + $submissionDetail->id_submission]); + + $submission = Submissions::find($submissionDetail->id_submission); + if (!$submission) { + Log::warning('Submission tidak ditemukan.', ['id_submission' => $submissionDetail->id_submission]); + return; + } + + $user = User::find($submission->id); + if (!$user || !$user->id_angkatan) { + Log::warning('User tidak ditemukan atau tidak memiliki id_angkatan.', ['id_submission' => $submissionDetail->id_submission]); + return; + } + + $id_angkatan = $user->id_angkatan; + + $alternatifCounts = SubmissionDetail::join('submission', 'submission.id_submission', '=', 'submission_detail.id_submission') + ->join('users', 'users.id', '=', 'submission.id') + ->where('users.id_angkatan', $id_angkatan) + ->selectRaw('id_alternatif, COUNT(*) as total_permintaan') + ->groupBy('id_alternatif') + ->pluck('total_permintaan', 'id_alternatif'); + + $id_alternatif = $submissionDetail->id_alternatif; + if (!$id_alternatif) { + Log::error('SubmissionDetail tidak memiliki id_alternatif', ['id_submission' => $submissionDetail->id_submission]); + return; + } + + $total_permintaan = $alternatifCounts[$id_alternatif] ?? 0; + + $subkriteriadata = ProfileMethod::join('subkriteria', 'subkriteria.id_subkriteria', '=', 'profile_method.id_subkriteria') + ->where('profile_method.id_alternatif', $id_alternatif) + ->value('subkriteria.subkriteria_name') ?? 0; + + $pemenuhanKuota = ProfileMethod::join('subkriteria', 'subkriteria.id_subkriteria', '=', 'profile_method.id_subkriteria') + ->where('profile_method.id_alternatif', $id_alternatif) + ->where('subkriteria.id_kriteria', 2) + ->value('subkriteria.subkriteria_name'); + + preg_match_all('/\d+/', $subkriteriadata, $matches); + $rangeStart = isset($matches[0][0]) ? (int) $matches[0][0] : 0; + $rangeEnd = isset($matches[0][1]) ? (int) $matches[0][1] : $rangeStart; + + // komen revisi + // $rasio = ($total_permintaan > 0 && $kuotaMax > 0) ? ($total_permintaan / $kuotaMax) * 100 : 0; + + $rasio = ($total_permintaan >= $rangeEnd) ? 'Terpenuhi' : 'Belum Terpenuhi'; + + if ($rasio !== $pemenuhanKuota) { + ProfileMethod::where('id_alternatif', $id_alternatif) + ->where('id_kriteria', 2) + ->update(['id_subkriteria' => 6]); + } + + Log::info('Perhitungan Kuota:', [ + 'id_users' => $user->id, + 'angkatan' => $user->id_angkatan, + 'id_alternatif' => $id_alternatif, + 'total_permintaan' => $total_permintaan, + 'subkriteria' => $subkriteriadata, + 'rangeStart' => $rangeStart, + 'rangeEnd' => $rangeEnd, + 'status' => $rasio, + 'pemenuhanKuota' => $pemenuhanKuota, + ]); + } catch (\Exception $e) { + Log::error('Terjadi kesalahan saat menghitung kuota:', [ + 'id_submission' => $submissionDetail->id_submission, + 'error' => $e->getMessage() + ]); + } + } + + /** + * Handle the SubmissionDetail "updated" event. + */ + public function updated(SubmissionDetail $submissionDetail): void + { + // + } + + /** + * Handle the SubmissionDetail "deleted" event. + */ + public function deleted(SubmissionDetail $submissionDetail): void + { + // + } + + /** + * Handle the SubmissionDetail "restored" event. + */ + public function restored(SubmissionDetail $submissionDetail): void + { + // + } + + /** + * Handle the SubmissionDetail "force deleted" event. + */ + public function forceDeleted(SubmissionDetail $submissionDetail): void + { + // + } +} \ No newline at end of file diff --git a/app/Observers/SubmissionsObserver.php b/app/Observers/SubmissionsObserver.php new file mode 100644 index 0000000..d33f4a1 --- /dev/null +++ b/app/Observers/SubmissionsObserver.php @@ -0,0 +1,57 @@ + [ + // UpdateProfileMethodOnSubmission::class, + // ], + // ]; + + /** + * Bootstrap services. + */ + public function boot(): void + { + } +} \ No newline at end of file diff --git a/app/View/Components/AppLayout.php b/app/View/Components/AppLayout.php new file mode 100644 index 0000000..de0d46f --- /dev/null +++ b/app/View/Components/AppLayout.php @@ -0,0 +1,17 @@ +handleCommand(new ArgvInput); + +exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..2d0be5e --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,25 @@ +withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + channels: __DIR__.'/../routes/channels.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware) { + $middleware->alias([ + 'role' => \Spatie\Permission\Middleware\RoleMiddleware::class, + 'permission' => \Spatie\Permission\Middleware\PermissionMiddleware::class, + 'role_or_permission' => \Spatie\Permission\Middleware\RoleOrPermissionMiddleware::class, + ]); + $middleware->redirectGuestsTo('/login'); + }) + ->withExceptions(function (Exceptions $exceptions) { + // + })->create(); \ No newline at end of file diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 0000000..002013b --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,6 @@ + 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' => env('APP_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(',', 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/config/auth.php b/config/auth.php new file mode 100644 index 0000000..0ba5d5d --- /dev/null +++ b/config/auth.php @@ -0,0 +1,115 @@ + [ + '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', App\Models\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 amount 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/config/broadcasting.php b/config/broadcasting.php new file mode 100644 index 0000000..ce23714 --- /dev/null +++ b/config/broadcasting.php @@ -0,0 +1,82 @@ + env('BROADCAST_CONNECTION', 'null'), + + /* + |-------------------------------------------------------------------------- + | Broadcast Connections + |-------------------------------------------------------------------------- + | + | Here you may define all of the broadcast connections that will be used + | to broadcast events to other systems or over WebSockets. Samples of + | each available type of connection are provided inside this array. + | + */ + + 'connections' => [ + + 'reverb' => [ + 'driver' => 'reverb', + 'key' => env('REVERB_APP_KEY'), + 'secret' => env('REVERB_APP_SECRET'), + 'app_id' => env('REVERB_APP_ID'), + 'options' => [ + 'host' => env('REVERB_HOST'), + 'port' => env('REVERB_PORT', 443), + 'scheme' => env('REVERB_SCHEME', 'https'), + 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'pusher' => [ + 'driver' => 'pusher', + 'key' => env('PUSHER_APP_KEY'), + 'secret' => env('PUSHER_APP_SECRET'), + 'app_id' => env('PUSHER_APP_ID'), + 'options' => [ + 'cluster' => env('PUSHER_APP_CLUSTER'), + 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', + 'port' => env('PUSHER_PORT', 443), + 'scheme' => env('PUSHER_SCHEME', 'https'), + 'encrypted' => true, + 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', + ], + 'client_options' => [ + // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html + ], + ], + + 'ably' => [ + 'driver' => 'ably', + 'key' => env('ABLY_KEY'), + ], + + 'log' => [ + 'driver' => 'log', + ], + + 'null' => [ + 'driver' => 'null', + ], + + ], + +]; \ No newline at end of file diff --git a/config/cache.php b/config/cache.php new file mode 100644 index 0000000..925f7d2 --- /dev/null +++ b/config/cache.php @@ -0,0 +1,108 @@ + 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", "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', + ], + + ], + + /* + |-------------------------------------------------------------------------- + | 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(env('APP_NAME', 'laravel'), '_').'_cache_'), + +]; diff --git a/config/database.php b/config/database.php new file mode 100644 index 0000000..125949e --- /dev/null +++ b/config/database.php @@ -0,0 +1,173 @@ + 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, + ], + + '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([ + 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([ + 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' => '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(env('APP_NAME', 'laravel'), '_').'_database_'), + ], + + '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'), + ], + + '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'), + ], + + ], + +]; diff --git a/config/filesystems.php b/config/filesystems.php new file mode 100644 index 0000000..b564035 --- /dev/null +++ b/config/filesystems.php @@ -0,0 +1,77 @@ + 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, + ], + + 'public' => [ + 'driver' => 'local', + 'root' => storage_path('app/public'), + 'url' => env('APP_URL').'/storage', + 'visibility' => 'public', + 'throw' => 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, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | 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/config/logging.php b/config/logging.php new file mode 100644 index 0000000..8d94292 --- /dev/null +++ b/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(',', 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', 'Laravel Log'), + '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, + 'formatter' => env('LOG_STDERR_FORMATTER'), + 'with' => [ + 'stream' => 'php://stderr', + ], + '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/config/mail.php b/config/mail.php new file mode 100644 index 0000000..df13d3d --- /dev/null +++ b/config/mail.php @@ -0,0 +1,116 @@ + 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', + 'url' => env('MAIL_URL'), + 'host' => env('MAIL_HOST', '127.0.0.1'), + 'port' => env('MAIL_PORT', 2525), + 'encryption' => env('MAIL_ENCRYPTION', 'tls'), + 'username' => env('MAIL_USERNAME'), + 'password' => env('MAIL_PASSWORD'), + 'timeout' => null, + 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url(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', + ], + ], + + 'roundrobin' => [ + 'transport' => 'roundrobin', + 'mailers' => [ + 'ses', + 'postmark', + ], + ], + + ], + + /* + |-------------------------------------------------------------------------- + | 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', 'Example'), + ], + +]; diff --git a/config/permission.php b/config/permission.php new file mode 100644 index 0000000..2a520f3 --- /dev/null +++ b/config/permission.php @@ -0,0 +1,186 @@ + [ + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * Eloquent model should be used to retrieve your permissions. Of course, it + * is often just the "Permission" model but you may use whatever you like. + * + * The model you want to use as a Permission model needs to implement the + * `Spatie\Permission\Contracts\Permission` contract. + */ + + 'permission' => Spatie\Permission\Models\Permission::class, + + /* + * When using the "HasRoles" trait from this package, we need to know which + * Eloquent model should be used to retrieve your roles. Of course, it + * is often just the "Role" model but you may use whatever you like. + * + * The model you want to use as a Role model needs to implement the + * `Spatie\Permission\Contracts\Role` contract. + */ + + 'role' => Spatie\Permission\Models\Role::class, + + ], + + 'table_names' => [ + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your roles. We have chosen a basic + * default value but you may easily change it to any table you like. + */ + + 'roles' => 'roles', + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * table should be used to retrieve your permissions. We have chosen a basic + * default value but you may easily change it to any table you like. + */ + + 'permissions' => 'permissions', + + /* + * When using the "HasPermissions" trait from this package, we need to know which + * table should be used to retrieve your models permissions. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'model_has_permissions' => 'model_has_permissions', + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your models roles. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'model_has_roles' => 'model_has_roles', + + /* + * When using the "HasRoles" trait from this package, we need to know which + * table should be used to retrieve your roles permissions. We have chosen a + * basic default value but you may easily change it to any table you like. + */ + + 'role_has_permissions' => 'role_has_permissions', + ], + + 'column_names' => [ + /* + * Change this if you want to name the related pivots other than defaults + */ + 'role_pivot_key' => null, //default 'role_id', + 'permission_pivot_key' => null, //default 'permission_id', + + /* + * Change this if you want to name the related model primary key other than + * `model_id`. + * + * For example, this would be nice if your primary keys are all UUIDs. In + * that case, name this `model_uuid`. + */ + + 'model_morph_key' => 'model_id', + + /* + * Change this if you want to use the teams feature and your related model's + * foreign key is other than `team_id`. + */ + + 'team_foreign_key' => 'team_id', + ], + + /* + * When set to true, the method for checking permissions will be registered on the gate. + * Set this to false if you want to implement custom logic for checking permissions. + */ + + 'register_permission_check_method' => true, + + /* + * When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered + * this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated + * NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it. + */ + 'register_octane_reset_listener' => false, + + /* + * Teams Feature. + * When set to true the package implements teams using the 'team_foreign_key'. + * If you want the migrations to register the 'team_foreign_key', you must + * set this to true before doing the migration. + * If you already did the migration then you must make a new migration to also + * add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions' + * (view the latest version of this package's migration file) + */ + + 'teams' => false, + + /* + * Passport Client Credentials Grant + * When set to true the package will use Passports Client to check permissions + */ + + 'use_passport_client_credentials' => false, + + /* + * When set to true, the required permission names are added to exception messages. + * This could be considered an information leak in some contexts, so the default + * setting is false here for optimum safety. + */ + + 'display_permission_in_exception' => false, + + /* + * When set to true, the required role names are added to exception messages. + * This could be considered an information leak in some contexts, so the default + * setting is false here for optimum safety. + */ + + 'display_role_in_exception' => false, + + /* + * By default wildcard permission lookups are disabled. + * See documentation to understand supported syntax. + */ + + 'enable_wildcard_permission' => false, + + /* + * The class to use for interpreting wildcard permissions. + * If you need to modify delimiters, override the class and specify its name here. + */ + // 'permission.wildcard_permission' => Spatie\Permission\WildcardPermission::class, + + /* Cache-specific settings */ + + 'cache' => [ + + /* + * By default all permissions are cached for 24 hours to speed up performance. + * When permissions or roles are updated the cache is flushed automatically. + */ + + 'expiration_time' => \DateInterval::createFromDateString('24 hours'), + + /* + * The cache key used to store all permissions. + */ + + 'key' => 'spatie.permission.cache', + + /* + * You may optionally indicate a specific cache driver to use for permission and + * role caching using any of the `store` drivers listed in the cache.php config + * file. Using 'default' here means to use the `default` set in cache.php. + */ + + 'store' => 'default', + ], +]; diff --git a/config/queue.php b/config/queue.php new file mode 100644 index 0000000..116bd8d --- /dev/null +++ b/config/queue.php @@ -0,0 +1,112 @@ + 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", "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, + ], + + ], + + /* + |-------------------------------------------------------------------------- + | 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/config/reverb.php b/config/reverb.php new file mode 100644 index 0000000..3fead53 --- /dev/null +++ b/config/reverb.php @@ -0,0 +1,92 @@ + env('REVERB_SERVER', 'reverb'), + + /* + |-------------------------------------------------------------------------- + | Reverb Servers + |-------------------------------------------------------------------------- + | + | Here you may define details for each of the supported Reverb servers. + | Each server has its own configuration options that are defined in + | the array below. You should ensure all the options are present. + | + */ + + 'servers' => [ + + 'reverb' => [ + 'host' => env('REVERB_SERVER_HOST', '0.0.0.0'), + 'port' => env('REVERB_SERVER_PORT', 8080), + 'hostname' => env('REVERB_HOST'), + 'options' => [ + 'tls' => [], + ], + 'max_request_size' => env('REVERB_MAX_REQUEST_SIZE', 10_000), + 'scaling' => [ + 'enabled' => env('REVERB_SCALING_ENABLED', false), + 'channel' => env('REVERB_SCALING_CHANNEL', 'reverb'), + 'server' => [ + 'url' => env('REDIS_URL'), + 'host' => env('REDIS_HOST', '127.0.0.1'), + 'port' => env('REDIS_PORT', '6379'), + 'username' => env('REDIS_USERNAME'), + 'password' => env('REDIS_PASSWORD'), + 'database' => env('REDIS_DB', '0'), + ], + ], + 'pulse_ingest_interval' => env('REVERB_PULSE_INGEST_INTERVAL', 15), + 'telescope_ingest_interval' => env('REVERB_TELESCOPE_INGEST_INTERVAL', 15), + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Reverb Applications + |-------------------------------------------------------------------------- + | + | Here you may define how Reverb applications are managed. If you choose + | to use the "config" provider, you may define an array of apps which + | your server will support, including their connection credentials. + | + */ + + 'apps' => [ + + 'provider' => 'config', + + 'apps' => [ + [ + 'key' => env('REVERB_APP_KEY'), + 'secret' => env('REVERB_APP_SECRET'), + 'app_id' => env('REVERB_APP_ID'), + 'options' => [ + 'host' => env('REVERB_HOST'), + 'port' => env('REVERB_PORT', 443), + 'scheme' => env('REVERB_SCHEME', 'https'), + 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', + ], + 'allowed_origins' => ['*'], + 'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60), + 'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30), + 'max_message_size' => env('REVERB_APP_MAX_MESSAGE_SIZE', 10_000), + ], + ], + + ], + +]; diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..764a82f --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,83 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, + 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + ], + +]; diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..5d5385b --- /dev/null +++ b/config/services.php @@ -0,0 +1,44 @@ + [ + 'client_id' => env('GOOGLE_CLIENT_ID'), + 'client_secret' => env('GOOGLE_CLIENT_SECRET'), + 'redirect' => env('GOOGLE_REDIRECT_URI'), + ], + + 'postmark' => [ + 'token' => env('POSTMARK_TOKEN'), + ], + + 'ses' => [ + 'key' => env('AWS_ACCESS_KEY_ID'), + 'secret' => env('AWS_SECRET_ACCESS_KEY'), + 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), + ], + + 'resend' => [ + 'key' => env('RESEND_KEY'), + ], + + 'slack' => [ + 'notifications' => [ + 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), + 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), + ], + ], + +]; \ No newline at end of file diff --git a/config/session.php b/config/session.php new file mode 100644 index 0000000..f0b6541 --- /dev/null +++ b/config/session.php @@ -0,0 +1,217 @@ + 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' => 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: "apc", "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(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 and all 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), + +]; diff --git a/config/tinker.php b/config/tinker.php new file mode 100644 index 0000000..c187942 --- /dev/null +++ b/config/tinker.php @@ -0,0 +1,50 @@ + [ + // App\Console\Commands\ExampleCommand::class, + ], + + /* + |-------------------------------------------------------------------------- + | Auto Aliased Classes + |-------------------------------------------------------------------------- + | + | Tinker will not automatically alias classes in your vendor namespaces + | but you may explicitly allow a subset of classes to get aliased by + | adding the names of each of those classes to the following list. + | + */ + + 'alias' => [ + // + ], + + /* + |-------------------------------------------------------------------------- + | Classes That Should Not Be Aliased + |-------------------------------------------------------------------------- + | + | Typically, Tinker automatically aliases classes as you require them in + | Tinker. However, you may wish to never alias certain classes, which + | you may accomplish by listing the classes in the following array. + | + */ + + 'dont_alias' => [ + 'App\Nova', + ], + +]; diff --git a/database/.gitignore b/database/.gitignore new file mode 100644 index 0000000..9b19b93 --- /dev/null +++ b/database/.gitignore @@ -0,0 +1 @@ +*.sqlite* diff --git a/database/factories/AlternatifFactory.php b/database/factories/AlternatifFactory.php new file mode 100644 index 0000000..ba0749d --- /dev/null +++ b/database/factories/AlternatifFactory.php @@ -0,0 +1,39 @@ + + */ +class AlternatifFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'nip' => $this->generateNIP(), + 'name' => $this->faker->name(), + ]; + } + + private function generateNIP(): string + { + $tahun_lahir = $this->faker->numberBetween(1950, 2000); + $bulan_lahir = str_pad($this->faker->numberBetween(1, 12), 2, '0', STR_PAD_LEFT); + $tanggal_lahir = str_pad($this->faker->numberBetween(1, 28), 2, '0', STR_PAD_LEFT); + + $tahun_pns = $this->faker->numberBetween(1990, 2023); + $bulan_pns = str_pad($this->faker->numberBetween(1, 12), 2, '0', STR_PAD_LEFT); + + $golongan = $this->faker->numberBetween(1, 4); + $urutan = str_pad($this->faker->numberBetween(1, 999), 3, '0', STR_PAD_LEFT); + + return "{$tahun_lahir}{$bulan_lahir}{$tanggal_lahir} {$tahun_pns}{$bulan_pns} {$golongan} {$urutan}"; + } +} diff --git a/database/factories/SubmissionDetailFactory.php b/database/factories/SubmissionDetailFactory.php new file mode 100644 index 0000000..acf77f4 --- /dev/null +++ b/database/factories/SubmissionDetailFactory.php @@ -0,0 +1,26 @@ + + */ +class SubmissionDetailFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'id_submission' => Submissions::factory(), + 'id_alternatif' => Alternatif::factory(), + ]; + } +} diff --git a/database/factories/SubmissionsFactory.php b/database/factories/SubmissionsFactory.php new file mode 100644 index 0000000..230015b --- /dev/null +++ b/database/factories/SubmissionsFactory.php @@ -0,0 +1,27 @@ + + */ +class SubmissionsFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'id' => User::factory(), + 'judul' => $this->faker->sentence(), + 'deskripsi' => $this->faker->text(), + 'prodi' => 'Teknik Informatika', + ]; + } +} \ No newline at end of file diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php new file mode 100644 index 0000000..1695866 --- /dev/null +++ b/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 [ + 'nim' => 'E421' . str_pad(random_int(0, 99999), 5, '0', STR_PAD_LEFT), + '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, + ]); + } +} \ No newline at end of file diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php new file mode 100644 index 0000000..11d6591 --- /dev/null +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -0,0 +1,51 @@ +id(); + $table->string('nim')->uniqid()->nullable(); + $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'); + } +}; \ No newline at end of file diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/database/migrations/0001_01_01_000001_create_cache_table.php new file mode 100644 index 0000000..b9c106b --- /dev/null +++ b/database/migrations/0001_01_01_000001_create_cache_table.php @@ -0,0 +1,35 @@ +string('key')->primary(); + $table->mediumText('value'); + $table->integer('expiration'); + }); + + Schema::create('cache_locks', function (Blueprint $table) { + $table->string('key')->primary(); + $table->string('owner'); + $table->integer('expiration'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('cache'); + Schema::dropIfExists('cache_locks'); + } +}; diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php new file mode 100644 index 0000000..425e705 --- /dev/null +++ b/database/migrations/0001_01_01_000002_create_jobs_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('queue')->index(); + $table->longText('payload'); + $table->unsignedTinyInteger('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/database/migrations/2025_01_04_162405_create_permission_tables.php b/database/migrations/2025_01_04_162405_create_permission_tables.php new file mode 100644 index 0000000..9c7044b --- /dev/null +++ b/database/migrations/2025_01_04_162405_create_permission_tables.php @@ -0,0 +1,140 @@ +engine('InnoDB'); + $table->bigIncrements('id'); // permission id + $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) + $table->string('guard_name'); // For MyISAM use string('guard_name', 25); + $table->timestamps(); + + $table->unique(['name', 'guard_name']); + }); + + Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) { + //$table->engine('InnoDB'); + $table->bigIncrements('id'); // role id + if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing + $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable(); + $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index'); + } + $table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format) + $table->string('guard_name'); // For MyISAM use string('guard_name', 25); + $table->timestamps(); + if ($teams || config('permission.testing')) { + $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']); + } else { + $table->unique(['name', 'guard_name']); + } + }); + + Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) { + $table->unsignedBigInteger($pivotPermission); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index'); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } else { + $table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'], + 'model_has_permissions_permission_model_type_primary'); + } + + }); + + Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) { + $table->unsignedBigInteger($pivotRole); + + $table->string('model_type'); + $table->unsignedBigInteger($columnNames['model_morph_key']); + $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + if ($teams) { + $table->unsignedBigInteger($columnNames['team_foreign_key']); + $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index'); + + $table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } else { + $table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'], + 'model_has_roles_role_model_type_primary'); + } + }); + + Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) { + $table->unsignedBigInteger($pivotPermission); + $table->unsignedBigInteger($pivotRole); + + $table->foreign($pivotPermission) + ->references('id') // permission id + ->on($tableNames['permissions']) + ->onDelete('cascade'); + + $table->foreign($pivotRole) + ->references('id') // role id + ->on($tableNames['roles']) + ->onDelete('cascade'); + + $table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary'); + }); + + app('cache') + ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null) + ->forget(config('permission.cache.key')); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + $tableNames = config('permission.table_names'); + + if (empty($tableNames)) { + throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.'); + } + + Schema::drop($tableNames['role_has_permissions']); + Schema::drop($tableNames['model_has_roles']); + Schema::drop($tableNames['model_has_permissions']); + Schema::drop($tableNames['roles']); + Schema::drop($tableNames['permissions']); + } +}; diff --git a/database/migrations/2025_01_04_164804_create_settings_table.php b/database/migrations/2025_01_04_164804_create_settings_table.php new file mode 100644 index 0000000..013fe79 --- /dev/null +++ b/database/migrations/2025_01_04_164804_create_settings_table.php @@ -0,0 +1,33 @@ +increments('id'); + $table->string('key')->nullable(); + $table->string('locale', 10)->nullable(); + $table->text('value')->nullable(); + $table->text('additional_value')->nullable(); + $table->json('json_value')->nullable(); + $table->json('additional_json_value')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('settings'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_01_05_054619_create_aspek_table.php b/database/migrations/2025_01_05_054619_create_aspek_table.php new file mode 100644 index 0000000..e739b18 --- /dev/null +++ b/database/migrations/2025_01_05_054619_create_aspek_table.php @@ -0,0 +1,31 @@ +id('id_aspek'); + $table->string('kode_aspek')->unique(); + $table->string('aspek_name')->unique(); + $table->float('persentase', 3, 2); + $table->string('keterangan'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('aspek'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_01_05_074311_create_kriteria_table.php b/database/migrations/2025_01_05_074311_create_kriteria_table.php new file mode 100644 index 0000000..3355810 --- /dev/null +++ b/database/migrations/2025_01_05_074311_create_kriteria_table.php @@ -0,0 +1,33 @@ +id('id_kriteria'); + $table->string('kode_kriteria')->unique(); + $table->unsignedBigInteger('id_aspek'); + $table->foreign('id_aspek')->references('id_aspek')->on('aspek')->onUpdate('cascade')->onDelete('cascade'); + $table->string('kriteria_name')->unique(); + $table->enum('tipe', ['Core Factor', 'Secondary Factor']); + $table->string('keterangan'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('kriteria'); + } +}; diff --git a/database/migrations/2025_01_07_032650_create_subkriteria_table.php b/database/migrations/2025_01_07_032650_create_subkriteria_table.php new file mode 100644 index 0000000..fc74420 --- /dev/null +++ b/database/migrations/2025_01_07_032650_create_subkriteria_table.php @@ -0,0 +1,30 @@ +id('id_subkriteria'); + $table->unsignedBigInteger('id_kriteria'); + $table->foreign('id_kriteria')->references('id_kriteria')->on('kriteria')->onUpdate('cascade')->onDelete('cascade'); + $table->string('subkriteria_name'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('subkriteria'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_01_07_113557_create_alternatif_table.php b/database/migrations/2025_01_07_113557_create_alternatif_table.php new file mode 100644 index 0000000..e5cd166 --- /dev/null +++ b/database/migrations/2025_01_07_113557_create_alternatif_table.php @@ -0,0 +1,29 @@ +id('id_alternatif'); + $table->string('nip')->unique(); + $table->string('name'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('alternatif'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_01_07_113638_create_profile_method_table.php b/database/migrations/2025_01_07_113638_create_profile_method_table.php new file mode 100644 index 0000000..ec9d919 --- /dev/null +++ b/database/migrations/2025_01_07_113638_create_profile_method_table.php @@ -0,0 +1,35 @@ +id('id_profile')->primary(); + $table->unsignedBigInteger('id_alternatif'); + $table->foreign('id_alternatif')->references('id_alternatif')->on('alternatif')->onUpdate('cascade')->onDelete('cascade'); + $table->unsignedBigInteger('id_kriteria'); + $table->foreign('id_kriteria')->references('id_kriteria')->on('kriteria')->onUpdate('cascade')->onDelete('cascade'); + $table->unsignedBigInteger('id_subkriteria'); + $table->foreign('id_subkriteria')->references('id_subkriteria')->on('subkriteria')->onUpdate('cascade')->onDelete('cascade'); + // $table->unsignedBigInteger('id_nilai'); + // $table->foreign('id_nilai')->references('id_nilai')->on('nilai')->onUpdate('cascade')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('profile_method'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_01_07_141505_create_submission_table.php b/database/migrations/2025_01_07_141505_create_submission_table.php new file mode 100644 index 0000000..9813d20 --- /dev/null +++ b/database/migrations/2025_01_07_141505_create_submission_table.php @@ -0,0 +1,32 @@ +id('id_submission'); + $table->unsignedBigInteger('id'); + $table->foreign('id')->references('id')->on('users')->onUpdate('cascade')->onDelete('cascade'); + $table->string('judul'); + $table->string('deskripsi'); + $table->string('prodi'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('submission'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_01_13_091619_create_personal_access_tokens_table.php b/database/migrations/2025_01_13_091619_create_personal_access_tokens_table.php new file mode 100644 index 0000000..e828ad8 --- /dev/null +++ b/database/migrations/2025_01_13_091619_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/migrations/2025_02_15_115744_add_google_id_to_users_table.php b/database/migrations/2025_02_15_115744_add_google_id_to_users_table.php new file mode 100644 index 0000000..1eb7a34 --- /dev/null +++ b/database/migrations/2025_02_15_115744_add_google_id_to_users_table.php @@ -0,0 +1,28 @@ +string('google_id')->after('remember_token')->nullable()->unique(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('google_id'); + }); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_02_20_234222_create_submission_detail_table.php b/database/migrations/2025_02_20_234222_create_submission_detail_table.php new file mode 100644 index 0000000..82b2d39 --- /dev/null +++ b/database/migrations/2025_02_20_234222_create_submission_detail_table.php @@ -0,0 +1,31 @@ +id('id_submission_detail'); + $table->unsignedBigInteger('id_submission'); + $table->foreign('id_submission')->references('id_submission')->on('submission')->onUpdate('cascade')->onDelete('cascade'); + $table->unsignedBigInteger('id_alternatif'); + $table->foreign('id_alternatif')->references('id_alternatif')->on('alternatif')->onUpdate('cascade')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('submission_detail'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_02_23_155439_create_nilai_table.php b/database/migrations/2025_02_23_155439_create_nilai_table.php new file mode 100644 index 0000000..06ce503 --- /dev/null +++ b/database/migrations/2025_02_23_155439_create_nilai_table.php @@ -0,0 +1,28 @@ +id('id_nilai'); + $table->float('value'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('nilai'); + } +}; diff --git a/database/migrations/2025_02_23_172822_add_id_nilai_column_to_subkriteria_table.php b/database/migrations/2025_02_23_172822_add_id_nilai_column_to_subkriteria_table.php new file mode 100644 index 0000000..e47c0fd --- /dev/null +++ b/database/migrations/2025_02_23_172822_add_id_nilai_column_to_subkriteria_table.php @@ -0,0 +1,29 @@ +unsignedBigInteger('id_nilai')->after('subkriteria_name'); + $table->foreign('id_nilai')->references('id_nilai')->on('nilai')->onUpdate('cascade')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('subkriteria', function (Blueprint $table) { + $table->dropColumn('id_nilai'); + }); + } +}; diff --git a/database/migrations/2025_02_25_121610_create_angkatan_table.php b/database/migrations/2025_02_25_121610_create_angkatan_table.php new file mode 100644 index 0000000..355ed90 --- /dev/null +++ b/database/migrations/2025_02_25_121610_create_angkatan_table.php @@ -0,0 +1,29 @@ +id('id_angkatan'); + $table->string('tahun'); + // $table->float('jumlah_mahasiswa'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('angkatan'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_02_25_143745_create_izin_pengajuan_table.php b/database/migrations/2025_02_25_143745_create_izin_pengajuan_table.php new file mode 100644 index 0000000..972040d --- /dev/null +++ b/database/migrations/2025_02_25_143745_create_izin_pengajuan_table.php @@ -0,0 +1,28 @@ +id('id_pengaturan'); + $table->enum('izin', ['on', 'off'])->default('on'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('izin_pengajuan'); + } +}; \ No newline at end of file diff --git a/database/migrations/2025_02_25_144050_add_angkatan_column_to_users_table.php b/database/migrations/2025_02_25_144050_add_angkatan_column_to_users_table.php new file mode 100644 index 0000000..8fed2f7 --- /dev/null +++ b/database/migrations/2025_02_25_144050_add_angkatan_column_to_users_table.php @@ -0,0 +1,29 @@ +unsignedBigInteger('id_angkatan')->after('password')->nullable(); + $table->foreign('id_angkatan')->references('id_angkatan')->on('angkatan')->onUpdate('cascade')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('id_angkatan'); + }); + } +}; \ No newline at end of file diff --git a/database/migrations/create_kuota_table b/database/migrations/create_kuota_table new file mode 100644 index 0000000..089a2fe --- /dev/null +++ b/database/migrations/create_kuota_table @@ -0,0 +1,30 @@ +id('id_kuota'); + // $table->unsignedBigInteger('id_angkatan')->nullable(); + // $table->foreign('id_angkatan')->references('id_angkatan')->on('angkatan')->onUpdate('cascade')->onDelete('cascade'); + // $table->float('max'); + // $table->timestamps(); + // }); + // } + + /** + * Reverse the migrations. + */ + // public function down(): void + // { + // Schema::dropIfExists('kuota'); + // } +}; diff --git a/database/migrations/noted.add_id_angkatan_column_to_profile_method_table b/database/migrations/noted.add_id_angkatan_column_to_profile_method_table new file mode 100644 index 0000000..edc5d74 --- /dev/null +++ b/database/migrations/noted.add_id_angkatan_column_to_profile_method_table @@ -0,0 +1,29 @@ +unsignedBigInteger('id_angkatan')->after('id_profile')->nullable(); + // $table->foreign('id_angkatan')->references('id_angkatan')->on('angkatan')->onUpdate('cascade')->onDelete('cascade'); + // }); + // } + + /** + * Reverse the migrations. + */ + // public function down(): void + // { + // Schema::table('profile_method', function (Blueprint $table) { + // $table->dropColumn('id_angkatan'); + // }); + // } +// }; \ No newline at end of file diff --git a/database/seeders/AlternatifSeeder.php b/database/seeders/AlternatifSeeder.php new file mode 100644 index 0000000..44cd839 --- /dev/null +++ b/database/seeders/AlternatifSeeder.php @@ -0,0 +1,139 @@ +insert([ + [ + 'nip' => '19711009 200312 1 001', + 'name' => 'Dr. Denny Trias Utomo, S.Si, M.T', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19740519 200312 1 002', + 'name' => 'Nugroho Setyo Wibowo, ST. MT', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19801212 200501 1 001', + 'name' => 'Prawidya Destarianto, S.Kom, M.T', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19810615 200604 1 002', + 'name' => 'Syamsul Arifin, S.Kom, M.Cs', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19700831 199803 1 001', + 'name' => 'Moh. Munih Dian Widianta, S.Kom, M.T', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19770929 200501 1 003', + 'name' => 'Didit Rahmat Hartadi, S.Kom, MT', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19781011 200501 2 002', + 'name' => 'Elly Antika, ST, M.Kom', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19830109 201803 1 001', + 'name' => 'Hermawan Arief Putranto, ST, MT', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19851128 200812 1 002', + 'name' => 'Aji Seto Arifianto, S.ST., M.T.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19860802 201504 2 002', + 'name' => 'Ratih Ayuninghemi, S.ST, M.Kom', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19920528 201803 2 001', + 'name' => 'Bety Etikasari, S.Pd, M.Pd', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19900227 201803 2 001', + 'name' => 'Trismayanti Dwi P, S.Kom, M.Cs', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19920302 201803 2 001', + 'name' => 'Zilvanhisna Emka Fitri, ST. MT', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19890710 201903 1 010', + 'name' => 'Ery Setiyawan Jullev Atmadji, S.Kom, M.Cs', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19940812 201903 1 013', + 'name' => 'Mukhamad Angga Gumilang, S. Pd., M. Eng.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19930508 202203 2 013', + 'name' => 'Dia Bitari Mei Yuana, S.ST., M.Tr.Kom.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19921227 202203 1 007', + 'name' => 'Choirul Huda, S.Kom., M.Kom.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19930831 202406 2 001', + 'name' => 'Arvita Agus Kurniasari, S.ST.,M.Tr.Kom', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19900702 202406 1 002', + 'name' => 'David Juli Ariyadi, S.Kom., M.Kom.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'nip' => '19950602 202406 2 005', + 'name' => 'Fatimatuzzahra, S.Kom., M.Kom', + 'created_at' => now(), + 'updated_at' => now(), + ], + ]); + } +} \ No newline at end of file diff --git a/database/seeders/AspekSeeder.php b/database/seeders/AspekSeeder.php new file mode 100644 index 0000000..fa0d80e --- /dev/null +++ b/database/seeders/AspekSeeder.php @@ -0,0 +1,43 @@ +insert([ + [ + 'kode_aspek' => 'A001', + 'aspek_name' => 'Kuota Bimbingan', + 'persentase' => 45, + 'keterangan' => 'Aspek ini mengukur jumlah maksimum bimbingan oleh seorang dosen pada satu periode tertentu.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'kode_aspek' => 'A002', + 'aspek_name' => 'Bidang Keahlian', + 'persentase' => 35, + 'keterangan' => 'Aspek ini mengukur kesesuaian antara topik skripsi mahasiswa dengan bidang keahlian dosen.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'kode_aspek' => 'A003', + 'aspek_name' => 'Riset Penelitian', + 'persentase' => 20, + 'keterangan' => 'Aspek ini mengukur pengalaman dosen dalam kegiatan riset dan penelitian yang relevan dengan topik skripsi mahasiswa.', + 'created_at' => now(), + 'updated_at' => now(), + ], + ]); + } +} \ No newline at end of file diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..ce239a0 --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,30 @@ +call([ + RoleAndPermissionSeeder::class, + SettingSeeder::class, + AdminSeeder::class, + MahasiswaSeeder::class, + AspekSeeder::class, + KriteriaSeeder::class, + SubkriteriaSeeder::class, + AlternatifSeeder::class, + ProfileMethodSeeder::class, + // KuotaSeeder::class, + ]); + } +} \ No newline at end of file diff --git a/database/seeders/KriteriaSeeder.php b/database/seeders/KriteriaSeeder.php new file mode 100644 index 0000000..fb45f17 --- /dev/null +++ b/database/seeders/KriteriaSeeder.php @@ -0,0 +1,74 @@ +insert([ + [ + 'kode_kriteria' => 'K001', + 'id_aspek' => 1, + 'kriteria_name' => 'Jumlah Kuota Bimbingan', + 'tipe' => 'Core Factor', + 'keterangan' => 'Jumlah maksimal kuota mahasiswa yang dapat dibimbing.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'kode_kriteria' => 'K002', + 'id_aspek' => 1, + 'kriteria_name' => 'Pemenuhan kuota bimbingan', + 'tipe' => 'Secondary Factor', + 'keterangan' => 'Pemenuhan jumlah maksimal kuota mahasiswa yang dapat dibimbing.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'kode_kriteria' => 'K003', + 'id_aspek' => 2, + 'kriteria_name' => 'Keahlian Utama', + 'tipe' => 'Core Factor', + 'keterangan' => 'keahlian utama dosen yang dimiliki dosen', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'kode_kriteria' => 'K004', + 'id_aspek' => 2, + 'kriteria_name' => 'Pengalaman Dalam Bidang', + 'tipe' => 'Secondary Factor', + 'keterangan' => 'Jumlah tahun pengalaman dalam keahlian utama yang dimiliki dosen', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'kode_kriteria' => 'K005', + 'id_aspek' => 3, + 'kriteria_name' => 'Proyek Penelitian', + 'tipe' => 'Core Factor', + 'keterangan' => 'Jumlah tahun keterlibatan dalam proyek penelitian.', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'kode_kriteria' => 'K006', + 'id_aspek' => 3, + 'kriteria_name' => 'Jumlah Publikasi Ilmiah', + 'tipe' => 'Secondary Factor', + 'keterangan' => 'Jumlah artikel yang dipublikasikan di Google Scholar.', + 'created_at' => now(), + 'updated_at' => now(), + ], + ]); + } +} \ No newline at end of file diff --git a/database/seeders/KuotaSeeder.php b/database/seeders/KuotaSeeder.php new file mode 100644 index 0000000..9cdf944 --- /dev/null +++ b/database/seeders/KuotaSeeder.php @@ -0,0 +1,39 @@ +count(); + if ($totalAlternatif == 0) { + return; + } + + $angkatans = DB::table('angkatan')->get(); + + foreach ($angkatans as $angkatan) { + $max = $angkatan->jumlah_mahasiswa / $totalAlternatif; + + DB::table('kuota')->insert([ + 'id_angkatan' => $angkatan->id_angkatan, + 'max' => $max, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + } +} diff --git a/database/seeders/ProfileMethodSeeder.php b/database/seeders/ProfileMethodSeeder.php new file mode 100644 index 0000000..07e8575 --- /dev/null +++ b/database/seeders/ProfileMethodSeeder.php @@ -0,0 +1,1284 @@ +exists() || !DB::table('kuota')->exists() || !DB::table('alternatif')->exists() || !DB::table('subkriteria')->exists()) { + // return; + // } + + // $kuotas = DB::table('kuota')->pluck('max', 'id_angkatan'); + + // $alternatifs = DB::table('alternatif')->pluck('id_alternatif'); + + // $subkriteriaData = DB::table('subkriteria')->pluck('id', 'subkriteria_name'); + + // $alternatifSub = [ + // 1 => [7, 10, 15, 19, 22], + // 2 => [7, 10, 15, 18, 23], + // 3 => [7, 10, 14, 18, 25], + // 4 => [7, 10, 14, 18, 21], + // 5 => [7, 10, 15, 19, 24], + // 6 => [7, 10, 14, 18, 22], + // 7 => [7, 10, 14, 19, 22], + // 8 => [7, 10, 12, 18, 23], + // 9 => [7, 10, 14, 18, 24], + // 10 => [7, 10, 13, 20, 22], + // 11 => [7, 10, 12, 18, 24], + // 12 => [7, 10, 12, 18, 25], + // 13 => [7, 10, 12, 18, 25], + // 14 => [7, 10, 12, 18, 23], + // 15 => [7, 10, 12, 18, 22], + // 16 => [7, 10, 11, 19, 21], + // 17 => [7, 10, 11, 17, 21], + // 18 => [7, 10, 11, 18, 23], + // 19 => [7, 10, 11, 16, 21], + // 20 => [7, 10, 11, 17, 21], + // ]; + + // $max_to_subkriteria = []; + + // foreach ($subkriteriaData as $name => $id) { + // if (preg_match('/(\d+)\s*-\s*(\d+)/', $name, $matches)) { + // $angka = (int) $matches[2]; + // $max_to_subkriteria[$angka] = $id; + // } + // } + + $alternatifs = [ + 1 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 15] + ], + 5 => [ + ['id_subkriteria' => 19], + ], + 6 => [ + ['id_subkriteria' => 22], + ] + ], + 2 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 15], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 23], + ] + ], + 3 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 14], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 25], + ] + ], + 4 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 14], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 21], + ] + ], + 5 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 15], + ], + 5 => [ + ['id_subkriteria' => 19], + ], + 6 => [ + ['id_subkriteria' => 24], + ] + ], + 6 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 14], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 22], + ] + ], + 7 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 14], + ], + 5 => [ + ['id_subkriteria' => 19], + ], + 6 => [ + ['id_subkriteria' => 22], + ] + ], + 8 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 12], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 23], + ] + ], + 9 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 14], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 24], + ] + ], + 10 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 13], + ], + 5 => [ + ['id_subkriteria' => 20], + ], + 6 => [ + ['id_subkriteria' => 22], + ] + ], + 11 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 12], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 24], + ] + ], + 12 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 12], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 25], + ] + ], + 13 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 12], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 25], + ] + ], + 14 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 12], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 23], + ] + ], + 15 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 12], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 22], + ] + ], + 16 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 11], + ], + 5 => [ + ['id_subkriteria' => 19], + ], + 6 => [ + ['id_subkriteria' => 21], + ] + ], + 17 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 11], + ], + 5 => [ + ['id_subkriteria' => 17], + ], + 6 => [ + ['id_subkriteria' => 21], + ] + ], + 18 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 11], + ], + 5 => [ + ['id_subkriteria' => 18], + ], + 6 => [ + ['id_subkriteria' => 23], + ] + ], + 19 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 11], + ], + 5 => [ + ['id_subkriteria' => 16], + ], + 6 => [ + ['id_subkriteria' => 21], + ] + ], + 20 => [ + 1 => [ + ['id_subkriteria' => 4], + ], + 2 => [ + ['id_subkriteria' => 7], + ], + 3 => [ + ['id_subkriteria' => 10], + ], + 4 => [ + ['id_subkriteria' => 11], + ], + 5 => [ + ['id_subkriteria' => 17], + ], + 6 => [ + ['id_subkriteria' => 21], + ] + ], + // 1 => [ + + // ], + // 2 => [ + // 1 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 15] + // ], + // 5 => [ + // ['id_subkriteria' => 19], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 2 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 15], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 23], + // ] + // ], + // 3 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 25], + // ] + // ], + // 4 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // 5 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 15], + // ], + // 5 => [ + // ['id_subkriteria' => 19], + // ], + // 6 => [ + // ['id_subkriteria' => 24], + // ] + // ], + // 6 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 7 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 19], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 8 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 23], + // ] + // ], + // 9 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 24], + // ] + // ], + // 10 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 13], + // ], + // 5 => [ + // ['id_subkriteria' => 20], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 11 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 24], + // ] + // ], + // 12 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 25], + // ] + // ], + // 13 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 25], + // ] + // ], + // 14 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 23], + // ] + // ], + // 15 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 16 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 19], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // 17 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 17], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // 18 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 23], + // ] + // ], + // 19 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 16], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // 20 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 4], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // ], + // 3 => [ + // 1 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 15] + // ], + // 5 => [ + // ['id_subkriteria' => 19], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 2 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 15], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 23], + // ] + // ], + // 3 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 25], + // ] + // ], + // 4 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // 5 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 15], + // ], + // 5 => [ + // ['id_subkriteria' => 19], + // ], + // 6 => [ + // ['id_subkriteria' => 24], + // ] + // ], + // 6 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 7 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 19], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 8 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 23], + // ] + // ], + // 9 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 14], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 24], + // ] + // ], + // 10 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 13], + // ], + // 5 => [ + // ['id_subkriteria' => 20], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 11 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 24], + // ] + // ], + // 12 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 25], + // ] + // ], + // 13 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 25], + // ] + // ], + // 14 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 23], + // ] + // ], + // 15 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 12], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 22], + // ] + // ], + // 16 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 19], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // 17 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 17], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // 18 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 18], + // ], + // 6 => [ + // ['id_subkriteria' => 23], + // ] + // ], + // 19 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 16], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // 20 => [ + // 1 => [ + // ['id_subkriteria' => 4], + // ], + // 2 => [ + // ['id_subkriteria' => 7], + // ], + // 3 => [ + // ['id_subkriteria' => 10], + // ], + // 4 => [ + // ['id_subkriteria' => 11], + // ], + // 5 => [ + // ['id_subkriteria' => 4], + // ], + // 6 => [ + // ['id_subkriteria' => 21], + // ] + // ], + // ], + ]; + + // foreach ($angkatan as $angkatanId => $alternatif) { + foreach ($alternatifs as $alternatifId => $kriteria) { + foreach ($kriteria as $kriteriaId => $valueProfile) { + foreach ($valueProfile as $value) { + ProfileMethod::create([ + // 'id_angkatan' => $angkatanId, + 'id_alternatif' => $alternatifId, + 'id_kriteria' => $kriteriaId, + 'id_subkriteria' => $value['id_subkriteria'], + ]); + } + } + } + // } + } +} \ No newline at end of file diff --git a/database/seeders/RoleAndPermissionSeeder.php b/database/seeders/RoleAndPermissionSeeder.php new file mode 100644 index 0000000..109a65e --- /dev/null +++ b/database/seeders/RoleAndPermissionSeeder.php @@ -0,0 +1,82 @@ +truncate(); + DB::table('model_has_permissions')->truncate(); + DB::table('role_has_permissions')->truncate(); + + $roles = ['admin', 'mahasiswa']; + + foreach ($roles as $role) { + Role::create(['name' => $role]); + } + + $customerRole = Role::where(['name' => 'mahasiswa'])->first(); + $customerPermissions = [ + 'pengajuan-create', + 'pengajuan-read', + 'pengajuan-update', + 'pengajuan-delete', + 'pemilihan-dosen-create', + 'pemilihan-dosen-read', + 'pemilihan-dosen-update', + 'pemilihan-dosen-delete', + ]; + + foreach ($customerPermissions as $permissionName) { + $permission = Permission::create(['name' => $permissionName]); + $customerRole->givePermissionTo($permission); + } + + $adminPermissions = [ + 'aspek-create', + 'aspek-read', + 'aspek-update', + 'aspek-delete', + 'kriteria-create', + 'kriteria-read', + 'kriteria-update', + 'kriteria-delete', + 'alternatif-create', + 'alternatif-read', + 'alternatif-update', + 'alternatif-delete', + 'profile-create', + 'profile-read', + 'profile-update', + 'profile-delete', + 'mahasiswa-create', + 'mahasiswa-read', + 'mahasiswa-update', + 'mahasiswa-delete', + 'setting-update', + 'website-update', + ]; + + $adminRole = Role::where(['name' => 'admin'])->first(); + foreach ($adminPermissions as $permissionName) { + $permission = Permission::create(['name' => $permissionName]); + $adminRole->givePermissionTo($permission); + } + + Schema::enableForeignKeyConstraints(); + } +} \ No newline at end of file diff --git a/database/seeders/SettingSeeder.php b/database/seeders/SettingSeeder.php new file mode 100644 index 0000000..87c4dc0 --- /dev/null +++ b/database/seeders/SettingSeeder.php @@ -0,0 +1,152 @@ +insert([ + [ + 'key' => 'about_us', + 'value' => '

Assalamu‘alaikum warohmatullahi wabarokatuh

+


+

Saibah Mulia Mandiri adalah biro perjalanan umrah dan haji + yang telah berpengalaman melayani para tamu Allah sejak tahun 1995. Saibah didirikan + oleh Ustadz Haji Bambang Riyanto dan Ibu Hj Aisyah Abdillah, dimana beliau berdua + sering menemani atau mendampingi

langsung para Jamaah Umroh maupun Haji.

+


+

Saibah dengan pembimbing yang sangat berpengalaman, mengutamakan kegiatan ibadah + selama di Tanah suci. Insya Allah selain beribadah dengan khusyuk jamaah akan + diperkenalkan dengan sejarah peradaban Islam dengan mengunjungi tempat-tempat + ibadah dan bersejarah bagi Umat Islam. Semoga Keinginan kita untuk beribadah + Umroh dan Haji dikabulkan oleh Allah . Dan semoga Ibadah Haji dan Umroh kita + di Mabrurkan oleh Allah. Aamiin Ya Rabbal ‘Aalamiin.

+


+

Wassalamu‘alaikum warohmatullahi wabarokatuh

+



+

Saibah Mulia Mandiri, Exclusive Umrah and Hajj Travel

+

Kantor Pusat Semarang: Jl. Hasanudin no. 243 B Semarang 50171, Indonesia

+


+

Telp : (024) 3521821, 081225442388

+

Telp/WA 081225442388

+

Telp/Fax (024)3521821,

+

E mail : admin@saibah.co.id

' + ], + [ + 'key' => 'privacy_policy', + 'value' => '

+ Pendukung Keputusan Pemilihan Dosen Pembimbing Skripsi D4-Teknik Informatika Menggunakan Metode Profile Matching + + built the SPK app as a Commercial app. This SERVICE is provided by Achmad Zakariya and is intended for use as is.

+


+

This page is used to inform visitors regarding our policies with the collection, use, and disclosure of Personal Information if anyone decided to use our Service.

+


+

If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is + used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy.

+


+

The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at Saibah unless otherwise defined in this Privacy Policy.

+


+

Information Collection and Use

+


+

For a better experience, while using our Service, we may require you to provide us with certain personally identifiable information. The information that we request will + be retained by us and used as described in this privacy policy.

+


+

The app does use third party services that may collect information used to identify you.

+


+

Link to privacy policy of third party service providers used by the app

+
    +
  • Google Play Services
  • +
  • Google
  • +
+


+

Log Data

+


+

We want to inform you that whenever you use our Service, in a case of an error in the app we collect data and information (through third party products) on your phone called Log Data. + This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating

' + ] + ]); + + DB::table('settings')->insert([ + [ + 'key' => 'email_setting', + 'json_value' => json_encode([ + 'logo' => '', + 'footer' => '

Mahasiswa Politeknik Negeri Jember, Jawa Timur, Indonesia

', + 'salutation' => "Salam,\r\nAchmad Zakariya" + ]) + ], + [ + 'key' => 'email_registration', + 'json_value' => json_encode([ + 'from' => 'info.spk-dosbing-profile-matching@gmail.com', + 'first_message' => '

Assalamualaikum,

Terimakasih telah mendaftar di platform ini. untuk melakukan proses sistem diharapkan mahasiswa berkenanan mengisi data pribadi guna kelengkapan data pada sistem, sehingga dapat dilakukan proses pemilihan dosen pembimbing dan pengajuan judul anda berjalan dengan lancar.

Terima Kasih

' + ]) + ] + ]); + + DB::table('nilai')->insert([ + [ + 'value' => 1, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'value' => 2, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'value' => 3, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'value' => 4, + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'value' => 5, + 'created_at' => now(), + 'updated_at' => now(), + ], + ]); + + DB::table('angkatan')->insert([ + [ + 'tahun' => '2021', + // 'jumlah_mahasiswa' => '185', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'tahun' => '2022', + // 'jumlah_mahasiswa' => '169', + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'tahun' => '2023', + // 'jumlah_mahasiswa' => '181', + 'created_at' => now(), + 'updated_at' => now(), + ] + ]); + + DB::table('izin_pengajuan')->insert([ + [ + 'izin' => 'off', + 'created_at' => now(), + 'updated_at' => now(), + ] + ]); + } +} \ No newline at end of file diff --git a/database/seeders/SubkriteriaSeeder.php b/database/seeders/SubkriteriaSeeder.php new file mode 100644 index 0000000..e7c83c8 --- /dev/null +++ b/database/seeders/SubkriteriaSeeder.php @@ -0,0 +1,80 @@ + [ + ['subkriteria' => '1 - 3 Mahasiswa', 'id_nilai' => 1], + ['subkriteria' => '4 - 6 Mahasiswa', 'id_nilai' => 2], + ['subkriteria' => '7 - 9 Mahasiswa', 'id_nilai' => 3], + ['subkriteria' => '10 - 12 Mahasiswa', 'id_nilai' => 4], + ['subkriteria' => '13 - 15 Mahasiswa', 'id_nilai' => 5], + ], + 2 => [ + // ['subkriteria' => 'Tersisa 20%', 'id_nilai' => 1], + // ['subkriteria' => 'Tersisa 40%', 'id_nilai' => 2], + // ['subkriteria' => 'Tersisa 60%', 'id_nilai' => 3], + // ['subkriteria' => 'Tersisa 80%', 'id_nilai' => 4], + // ['subkriteria' => 'Tersisa 100%', 'id_nilai' => 5], + ['subkriteria' => 'Terpenuhi', 'id_nilai' => '1'], + ['subkriteria' => 'Belum Terpenuhi', 'id_nilai' => '2'], + ], + 3 => [ + ['subkriteria' => 'Information Technology Entrepreneur', 'id_nilai' => 1], + ['subkriteria' => 'Software Quality Assurance Analyst', 'id_nilai' => 2], + ['subkriteria' => 'Software Engineer', 'id_nilai' => 3], + ], + 4 => [ + ['subkriteria' => '2022 - 2027', 'id_nilai' => 1], + ['subkriteria' => '2016 - 2021', 'id_nilai' => 2], + ['subkriteria' => '2010 - 2015', 'id_nilai' => 3], + ['subkriteria' => '2004 - 2009', 'id_nilai' => 4], + ['subkriteria' => '1998 - 2003', 'id_nilai' => 5], + ], + 5 => [ + ['subkriteria' => '1 - 3 Tahun', 'id_nilai' => 1], + ['subkriteria' => '4 - 7 Tahun', 'id_nilai' => 2], + ['subkriteria' => '8 - 11 Tahun', 'id_nilai' => 3], + ['subkriteria' => '12 - 15 Tahun', 'id_nilai' => 4], + ['subkriteria' => 'Lebih dari 15 Tahun', 'id_nilai' => 5], + // ['subkriteria' => '4 Tahun'], + // ['subkriteria' => '5 Tahun'], + // ['subkriteria' => '8 Tahun'], + // ['subkriteria' => '10 Tahun'], + // ['subkriteria' => '11 Tahun'], + // ['subkriteria' => '12 Tahun'], + // ['subkriteria' => '15 Tahun'], + // ['subkriteria' => '18 Tahun'], + // ['subkriteria' => '27 Tahun'], + ], + 6 => [ + ['subkriteria' => '0 - 50 Jurnal', 'id_nilai' => 1], + ['subkriteria' => '51 - 101 Jurnal', 'id_nilai' => 2], + ['subkriteria' => '102 - 152 Jurnal', 'id_nilai' => 3], + ['subkriteria' => '153 - 203 Jurnal', 'id_nilai' => 4], + ['subkriteria' => 'Lebih dari 203 Jurnal', 'id_nilai' => 5], + ] + ]; + + foreach ($kriteriaData as $kriteriaId => $subkriterias) { + foreach ($subkriterias as $value) { + Subkriteria::create([ + 'id_kriteria' => $kriteriaId, + 'subkriteria_name' => $value['subkriteria'], + 'id_nilai' => $value['id_nilai'] + ]); + } + } + } +} \ No newline at end of file diff --git a/database/seeders/User/AdminSeeder.php b/database/seeders/User/AdminSeeder.php new file mode 100644 index 0000000..3caebef --- /dev/null +++ b/database/seeders/User/AdminSeeder.php @@ -0,0 +1,32 @@ +truncate(); + DB::statement('SET FOREIGN_KEY_CHECKS=1;'); + + $admin = User::firstOrCreate([ + 'nim' => 'E4211320', + 'email' => 'e41211320@student.polije.ac.id', + ], [ + 'name' => 'e41211320', + 'password' => Hash::make('p4ssword'), + 'email_verified_at' => now(), + ]); + + $admin->assignRole(User::ROLE_ADMIN); + } +} \ No newline at end of file diff --git a/database/seeders/User/MahasiswaSeeder.php b/database/seeders/User/MahasiswaSeeder.php new file mode 100644 index 0000000..eacca8f --- /dev/null +++ b/database/seeders/User/MahasiswaSeeder.php @@ -0,0 +1,99 @@ + $data[1], + 'name' => $name, + 'email' => "$nim@student.polije.ac.id", + 'password' => bcrypt($nim), + 'id_angkatan' => $id_angkatan, + 'email_verified_at' => now(), + ]); + + $user->assignRole(User::ROLE_MAHASISWA); + } + } +} diff --git a/docker/8.0/Dockerfile b/docker/8.0/Dockerfile new file mode 100644 index 0000000..84a5a27 --- /dev/null +++ b/docker/8.0/Dockerfile @@ -0,0 +1,69 @@ +FROM ubuntu:20.04 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=20 +ARG POSTGRES_VERSION=17 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC +ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" +ENV SUPERVISOR_PHP_USER="sail" + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN echo "Acquire::http::Pipeline-Depth 0;" > /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::http::No-Cache true;" >> /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::BrokenProxy true;" >> /etc/apt/apt.conf.d/99custom + +RUN apt-get update && apt-get upgrade -y \ + && mkdir -p /etc/apt/keyrings \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ + && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu focal main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.0-cli php8.0-dev \ + php8.0-pgsql php8.0-sqlite3 php8.0-gd php8.0-imagick \ + php8.0-curl php8.0-memcached php8.0-mongodb \ + php8.0-imap php8.0-mysql php8.0-mbstring \ + php8.0-xml php8.0-zip php8.0-bcmath php8.0-soap \ + php8.0-intl php8.0-readline php8.0-pcov \ + php8.0-msgpack php8.0-igbinary php8.0-ldap \ + php8.0-redis php8.0-swoole php8.0-xdebug \ + && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && npm install -g bun \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarnkey.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt focal-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y mysql-client \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN update-alternatives --set php /usr/bin/php8.0 + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.0 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.0/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 80/tcp + +ENTRYPOINT ["start-container"] diff --git a/docker/8.0/php.ini b/docker/8.0/php.ini new file mode 100644 index 0000000..0d8ce9e --- /dev/null +++ b/docker/8.0/php.ini @@ -0,0 +1,5 @@ +[PHP] +post_max_size = 100M +upload_max_filesize = 100M +variables_order = EGPCS +pcov.directory = . diff --git a/docker/8.0/start-container b/docker/8.0/start-container new file mode 100644 index 0000000..40c55df --- /dev/null +++ b/docker/8.0/start-container @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then + echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." + exit 1 +fi + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + if [ "$SUPERVISOR_PHP_USER" = "root" ]; then + exec "$@" + else + exec gosu $WWWUSER "$@" + fi +else + exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/docker/8.0/supervisord.conf b/docker/8.0/supervisord.conf new file mode 100644 index 0000000..656da8a --- /dev/null +++ b/docker/8.0/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=%(ENV_SUPERVISOR_PHP_COMMAND)s +user=%(ENV_SUPERVISOR_PHP_USER)s +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docker/8.1/Dockerfile b/docker/8.1/Dockerfile new file mode 100644 index 0000000..591849e --- /dev/null +++ b/docker/8.1/Dockerfile @@ -0,0 +1,68 @@ +FROM ubuntu:22.04 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=20 +ARG POSTGRES_VERSION=17 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC +ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" +ENV SUPERVISOR_PHP_USER="sail" + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN echo "Acquire::http::Pipeline-Depth 0;" > /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::http::No-Cache true;" >> /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::BrokenProxy true;" >> /etc/apt/apt.conf.d/99custom + +RUN apt-get update && apt-get upgrade -y \ + && mkdir -p /etc/apt/keyrings \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ + && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /usr/share/keyrings/ppa_ondrej_php.gpg > /dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.1-cli php8.1-dev \ + php8.1-pgsql php8.1-sqlite3 php8.1-gd php8.1-imagick \ + php8.1-curl php8.1-mongodb \ + php8.1-imap php8.1-mysql php8.1-mbstring \ + php8.1-xml php8.1-zip php8.1-bcmath php8.1-soap \ + php8.1-intl php8.1-readline \ + php8.1-ldap \ + php8.1-msgpack php8.1-igbinary php8.1-redis php8.1-swoole \ + php8.1-memcached php8.1-pcov php8.1-xdebug \ + && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && npm install -g bun \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y mysql-client \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.1 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.1/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 80/tcp + +ENTRYPOINT ["start-container"] diff --git a/docker/8.1/php.ini b/docker/8.1/php.ini new file mode 100644 index 0000000..0d8ce9e --- /dev/null +++ b/docker/8.1/php.ini @@ -0,0 +1,5 @@ +[PHP] +post_max_size = 100M +upload_max_filesize = 100M +variables_order = EGPCS +pcov.directory = . diff --git a/docker/8.1/start-container b/docker/8.1/start-container new file mode 100644 index 0000000..40c55df --- /dev/null +++ b/docker/8.1/start-container @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then + echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." + exit 1 +fi + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + if [ "$SUPERVISOR_PHP_USER" = "root" ]; then + exec "$@" + else + exec gosu $WWWUSER "$@" + fi +else + exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/docker/8.1/supervisord.conf b/docker/8.1/supervisord.conf new file mode 100644 index 0000000..656da8a --- /dev/null +++ b/docker/8.1/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=%(ENV_SUPERVISOR_PHP_COMMAND)s +user=%(ENV_SUPERVISOR_PHP_USER)s +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docker/8.2/Dockerfile b/docker/8.2/Dockerfile new file mode 100644 index 0000000..7ab16c7 --- /dev/null +++ b/docker/8.2/Dockerfile @@ -0,0 +1,69 @@ +FROM ubuntu:22.04 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=20 +ARG POSTGRES_VERSION=17 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC +ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" +ENV SUPERVISOR_PHP_USER="sail" + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN echo "Acquire::http::Pipeline-Depth 0;" > /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::http::No-Cache true;" >> /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::BrokenProxy true;" >> /etc/apt/apt.conf.d/99custom + +RUN apt-get update && apt-get upgrade -y \ + && mkdir -p /etc/apt/keyrings \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ + && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.2-cli php8.2-dev \ + php8.2-pgsql php8.2-sqlite3 php8.2-gd php8.2-imagick \ + php8.2-curl php8.2-mongodb \ + php8.2-imap php8.2-mysql php8.2-mbstring \ + php8.2-xml php8.2-zip php8.2-bcmath php8.2-soap \ + php8.2-intl php8.2-readline \ + php8.2-ldap \ + php8.2-msgpack php8.2-igbinary php8.2-redis php8.2-swoole \ + php8.2-memcached php8.2-pcov php8.2-xdebug \ + && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && npm install -g pnpm \ + && npm install -g bun \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y mysql-client \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.2/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 80/tcp + +ENTRYPOINT ["start-container"] diff --git a/docker/8.2/php.ini b/docker/8.2/php.ini new file mode 100644 index 0000000..0d8ce9e --- /dev/null +++ b/docker/8.2/php.ini @@ -0,0 +1,5 @@ +[PHP] +post_max_size = 100M +upload_max_filesize = 100M +variables_order = EGPCS +pcov.directory = . diff --git a/docker/8.2/start-container b/docker/8.2/start-container new file mode 100644 index 0000000..40c55df --- /dev/null +++ b/docker/8.2/start-container @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then + echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." + exit 1 +fi + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + if [ "$SUPERVISOR_PHP_USER" = "root" ]; then + exec "$@" + else + exec gosu $WWWUSER "$@" + fi +else + exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/docker/8.2/supervisord.conf b/docker/8.2/supervisord.conf new file mode 100644 index 0000000..656da8a --- /dev/null +++ b/docker/8.2/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=%(ENV_SUPERVISOR_PHP_COMMAND)s +user=%(ENV_SUPERVISOR_PHP_USER)s +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docker/8.3/Dockerfile b/docker/8.3/Dockerfile new file mode 100644 index 0000000..46a5ca4 --- /dev/null +++ b/docker/8.3/Dockerfile @@ -0,0 +1,70 @@ +FROM ubuntu:22.04 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=20 +ARG MYSQL_CLIENT="mysql-client" +ARG POSTGRES_VERSION=17 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC +ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" +ENV SUPERVISOR_PHP_USER="sail" + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN echo "Acquire::http::Pipeline-Depth 0;" > /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::http::No-Cache true;" >> /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::BrokenProxy true;" >> /etc/apt/apt.conf.d/99custom + +RUN apt-get update && apt-get upgrade -y \ + && mkdir -p /etc/apt/keyrings \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ + && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.3-cli php8.3-dev \ + php8.3-pgsql php8.3-sqlite3 php8.3-gd \ + php8.3-curl php8.3-mongodb \ + php8.3-imap php8.3-mysql php8.3-mbstring \ + php8.3-xml php8.3-zip php8.3-bcmath php8.3-soap \ + php8.3-intl php8.3-readline \ + php8.3-ldap \ + php8.3-msgpack php8.3-igbinary php8.3-redis \ + php8.3-memcached php8.3-pcov php8.3-imagick php8.3-xdebug php8.3-swoole \ + && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && npm install -g pnpm \ + && npm install -g bun \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y $MYSQL_CLIENT \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.3 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.3/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 80/tcp + +ENTRYPOINT ["start-container"] diff --git a/docker/8.3/php.ini b/docker/8.3/php.ini new file mode 100644 index 0000000..0d8ce9e --- /dev/null +++ b/docker/8.3/php.ini @@ -0,0 +1,5 @@ +[PHP] +post_max_size = 100M +upload_max_filesize = 100M +variables_order = EGPCS +pcov.directory = . diff --git a/docker/8.3/start-container b/docker/8.3/start-container new file mode 100644 index 0000000..40c55df --- /dev/null +++ b/docker/8.3/start-container @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then + echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." + exit 1 +fi + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + if [ "$SUPERVISOR_PHP_USER" = "root" ]; then + exec "$@" + else + exec gosu $WWWUSER "$@" + fi +else + exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/docker/8.3/supervisord.conf b/docker/8.3/supervisord.conf new file mode 100644 index 0000000..656da8a --- /dev/null +++ b/docker/8.3/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=%(ENV_SUPERVISOR_PHP_COMMAND)s +user=%(ENV_SUPERVISOR_PHP_USER)s +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docker/8.4/Dockerfile b/docker/8.4/Dockerfile new file mode 100644 index 0000000..ec63236 --- /dev/null +++ b/docker/8.4/Dockerfile @@ -0,0 +1,71 @@ +FROM ubuntu:22.04 + +LABEL maintainer="Taylor Otwell" + +ARG WWWGROUP +ARG NODE_VERSION=20 +ARG MYSQL_CLIENT="mysql-client" +ARG POSTGRES_VERSION=17 + +WORKDIR /var/www/html + +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC +ENV SUPERVISOR_PHP_COMMAND="/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan serve --host=0.0.0.0 --port=80" +ENV SUPERVISOR_PHP_USER="sail" + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN echo "Acquire::http::Pipeline-Depth 0;" > /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::http::No-Cache true;" >> /etc/apt/apt.conf.d/99custom && \ + echo "Acquire::BrokenProxy true;" >> /etc/apt/apt.conf.d/99custom + +RUN apt-get update && apt-get upgrade -y \ + && mkdir -p /etc/apt/keyrings \ + && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 dnsutils librsvg2-bin fswatch ffmpeg nano \ + && curl -sS 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c' | gpg --dearmor | tee /etc/apt/keyrings/ppa_ondrej_php.gpg > /dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \ + && apt-get update \ + && apt-get install -y php8.4-cli php8.4-dev \ + php8.4-pgsql php8.4-sqlite3 php8.4-gd \ + php8.4-curl php8.4-mongodb \ + php8.4-imap php8.4-mysql php8.4-mbstring \ + php8.4-xml php8.4-zip php8.4-bcmath php8.4-soap \ + php8.4-intl php8.4-readline \ + php8.4-ldap \ + php8.4-msgpack php8.4-igbinary php8.4-redis \ +# php8.4-swoole \ + php8.4-memcached php8.4-pcov php8.4-imagick php8.4-xdebug \ + && curl -sLS https://getcomposer.org/installer | php -- --install-dir=/usr/bin/ --filename=composer \ + && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_VERSION.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y nodejs \ + && npm install -g npm \ + && npm install -g pnpm \ + && npm install -g bun \ + && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /etc/apt/keyrings/yarn.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \ + && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /etc/apt/keyrings/pgdg.gpg >/dev/null \ + && echo "deb [signed-by=/etc/apt/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ + && apt-get update \ + && apt-get install -y yarn \ + && apt-get install -y $MYSQL_CLIENT \ + && apt-get install -y postgresql-client-$POSTGRES_VERSION \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.4 + +RUN groupadd --force -g $WWWGROUP sail +RUN useradd -ms /bin/bash --no-user-group -g $WWWGROUP -u 1337 sail + +COPY start-container /usr/local/bin/start-container +COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY php.ini /etc/php/8.4/cli/conf.d/99-sail.ini +RUN chmod +x /usr/local/bin/start-container + +EXPOSE 80/tcp + +ENTRYPOINT ["start-container"] diff --git a/docker/8.4/php.ini b/docker/8.4/php.ini new file mode 100644 index 0000000..0d8ce9e --- /dev/null +++ b/docker/8.4/php.ini @@ -0,0 +1,5 @@ +[PHP] +post_max_size = 100M +upload_max_filesize = 100M +variables_order = EGPCS +pcov.directory = . diff --git a/docker/8.4/start-container b/docker/8.4/start-container new file mode 100644 index 0000000..40c55df --- /dev/null +++ b/docker/8.4/start-container @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [ "$SUPERVISOR_PHP_USER" != "root" ] && [ "$SUPERVISOR_PHP_USER" != "sail" ]; then + echo "You should set SUPERVISOR_PHP_USER to either 'sail' or 'root'." + exit 1 +fi + +if [ ! -z "$WWWUSER" ]; then + usermod -u $WWWUSER sail +fi + +if [ ! -d /.composer ]; then + mkdir /.composer +fi + +chmod -R ugo+rw /.composer + +if [ $# -gt 0 ]; then + if [ "$SUPERVISOR_PHP_USER" = "root" ]; then + exec "$@" + else + exec gosu $WWWUSER "$@" + fi +else + exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf +fi diff --git a/docker/8.4/supervisord.conf b/docker/8.4/supervisord.conf new file mode 100644 index 0000000..656da8a --- /dev/null +++ b/docker/8.4/supervisord.conf @@ -0,0 +1,14 @@ +[supervisord] +nodaemon=true +user=root +logfile=/var/log/supervisor/supervisord.log +pidfile=/var/run/supervisord.pid + +[program:php] +command=%(ENV_SUPERVISOR_PHP_COMMAND)s +user=%(ENV_SUPERVISOR_PHP_USER)s +environment=LARAVEL_SAIL="1" +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docker/mariadb/create-testing-database.sh b/docker/mariadb/create-testing-database.sh new file mode 100644 index 0000000..d3b19d9 --- /dev/null +++ b/docker/mariadb/create-testing-database.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +/usr/bin/mariadb --user=root --password="$MYSQL_ROOT_PASSWORD" <<-EOSQL + CREATE DATABASE IF NOT EXISTS testing; + GRANT ALL PRIVILEGES ON \`testing%\`.* TO '$MYSQL_USER'@'%'; +EOSQL diff --git a/docker/mysql/create-testing-database.sh b/docker/mysql/create-testing-database.sh new file mode 100644 index 0000000..aeb1826 --- /dev/null +++ b/docker/mysql/create-testing-database.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +mysql --user=root --password="$MYSQL_ROOT_PASSWORD" <<-EOSQL + CREATE DATABASE IF NOT EXISTS testing; + GRANT ALL PRIVILEGES ON \`testing%\`.* TO '$MYSQL_USER'@'%'; +EOSQL diff --git a/docker/pgsql/create-testing-database.sql b/docker/pgsql/create-testing-database.sql new file mode 100644 index 0000000..d84dc07 --- /dev/null +++ b/docker/pgsql/create-testing-database.sql @@ -0,0 +1,2 @@ +SELECT 'CREATE DATABASE testing' +WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'testing')\gexec diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..66fd095 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4058 @@ +{ + "name": "spk_pm_dosbing", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@popperjs/core": "^2.11.8", + "@preline/accordion": "^2.5.2", + "@preline/datatable": "^2.5.2", + "@preline/dropdown": "^2.5.2", + "@preline/layout-splitter": "^2.5.2", + "@preline/stepper": "^2.5.2", + "@preline/tooltip": "^2.5.2", + "datatables.net-buttons": "^3.2.2", + "datatables.net-dt": "^2.2.2", + "gsap": "^3.12.7", + "jquery": "^3.7.1", + "jszip": "^3.10.1", + "lenis": "^1.1.20", + "pdfmake": "^0.2.18", + "preline": "^2.7.0", + "simple-datatables": "^9.2.1", + "swiper": "^11.2.3" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.2", + "alpinejs": "^3.4.2", + "autoprefixer": "^10.4.20", + "axios": "^1.7.4", + "concurrently": "^9.0.1", + "flowbite": "^2.5.2", + "laravel-echo": "^2.0.2", + "laravel-vite-plugin": "^1.2.0", + "postcss": "^8.4.49", + "prettier": "^3.5.2", + "pusher-js": "^8.4.0", + "tailwindcss": "^3.4.15", + "vite": "^6.1.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz", + "integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz", + "integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz", + "integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz", + "integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz", + "integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz", + "integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz", + "integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz", + "integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz", + "integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz", + "integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz", + "integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz", + "integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz", + "integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz", + "integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz", + "integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz", + "integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz", + "integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz", + "integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz", + "integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz", + "integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz", + "integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz", + "integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz", + "integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz", + "integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz", + "integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@foliojs-fork/fontkit": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.2.tgz", + "integrity": "sha512-IfB5EiIb+GZk+77TRB86AHroVaqfq8JRFlUbz0WEwsInyCG0epX2tCPOy+UfaWPju30DeVoUAXfzWXmhn753KA==", + "license": "MIT", + "dependencies": { + "@foliojs-fork/restructure": "^2.0.2", + "brotli": "^1.2.0", + "clone": "^1.0.4", + "deep-equal": "^1.0.0", + "dfa": "^1.2.0", + "tiny-inflate": "^1.0.2", + "unicode-properties": "^1.2.2", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/@foliojs-fork/linebreak": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@foliojs-fork/linebreak/-/linebreak-1.1.2.tgz", + "integrity": "sha512-ZPohpxxbuKNE0l/5iBJnOAfUaMACwvUIKCvqtWGKIMv1lPYoNjYXRfhi9FeeV9McBkBLxsMFWTVVhHJA8cyzvg==", + "license": "MIT", + "dependencies": { + "base64-js": "1.3.1", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/@foliojs-fork/pdfkit": { + "version": "0.15.3", + "resolved": "https://registry.npmjs.org/@foliojs-fork/pdfkit/-/pdfkit-0.15.3.tgz", + "integrity": "sha512-Obc0Wmy3bm7BINFVvPhcl2rnSSK61DQrlHU8aXnAqDk9LCjWdUOPwhgD8Ywz5VtuFjRxmVOM/kQ/XLIBjDvltw==", + "license": "MIT", + "dependencies": { + "@foliojs-fork/fontkit": "^1.9.2", + "@foliojs-fork/linebreak": "^1.1.1", + "crypto-js": "^4.2.0", + "jpeg-exif": "^1.1.4", + "png-js": "^1.0.0" + } + }, + "node_modules/@foliojs-fork/restructure": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@foliojs-fork/restructure/-/restructure-2.0.2.tgz", + "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==", + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@preline/accordion": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@preline/accordion/-/accordion-2.5.2.tgz", + "integrity": "sha512-5+C8c+vWyUMCdBOWMoXEIPT5ICSZMZ18G3AUjtoGI6UgSOZ+QpZMh/rpfPLoZkg0TrUhuD6XhBDvoqpCyH66Aw==", + "license": "Licensed under MIT and Preline UI Fair Use License" + }, + "node_modules/@preline/datatable": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@preline/datatable/-/datatable-2.5.2.tgz", + "integrity": "sha512-F78PBlGyLRqfqbU2UWkTdPBl7hCn6Ne7jsEQecY3BhhT9C1xtf01RSlNY+avMijBBiqT0UdhxTApcBaC2YEthA==", + "license": "Licensed under MIT and Preline UI Fair Use License" + }, + "node_modules/@preline/dropdown": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@preline/dropdown/-/dropdown-2.5.2.tgz", + "integrity": "sha512-eFrJxIlvIofn8ge6Fr/5jLDLdKtiTx69U/JsIp1jOPTUheuBnyyuyjJGASNAAJjx+VBHnn/V3538ZeK7E/VKAQ==", + "license": "Licensed under MIT and Preline UI Fair Use License" + }, + "node_modules/@preline/layout-splitter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@preline/layout-splitter/-/layout-splitter-2.5.2.tgz", + "integrity": "sha512-3y+Xiz2XmoDS24XGSFvzJf0xP4f05jLRLY8PV+mzvoDcLOjCSLBPCkaOY4Eirj27Ru9EmG9/zcCUHTmIfGEiLQ==", + "license": "Licensed under MIT and Preline UI Fair Use License" + }, + "node_modules/@preline/stepper": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@preline/stepper/-/stepper-2.5.2.tgz", + "integrity": "sha512-QqUJqreUJp6teXf2TyKmiFNNW2xvuZLZ9WgL3w1j98lWwa5lRfUhVu2NMQ2ffQsVV5OVDeVcGBznO41sWYp5fw==", + "license": "Licensed under MIT and Preline UI Fair Use License" + }, + "node_modules/@preline/tooltip": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@preline/tooltip/-/tooltip-2.5.2.tgz", + "integrity": "sha512-dXqgBRyYZ2Y7LOZxd+QM6jRzRBUqkoT+o4zgaKXtPVessNQjRjulJqaPRhjfW8XSIEz4f5PP781dazvw1Zj0vQ==", + "license": "Licensed under MIT and Preline UI Fair Use License" + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.1.tgz", + "integrity": "sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", + "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", + "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", + "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", + "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", + "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", + "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", + "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", + "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", + "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", + "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", + "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", + "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", + "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", + "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", + "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", + "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", + "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", + "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", + "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", + "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.10.tgz", + "integrity": "sha512-utI1ONF6uf/pPNO68kmN1b8rEwNXv3czukalo8VtJH8ksIkZXr3Q3VYudZLkCsDd4Wku120uF02hYK25XGPorw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/alpinejs": { + "version": "3.14.8", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.14.8.tgz", + "integrity": "sha512-wT2fuP2DXpGk/jKaglwy7S/IJpm1FD+b7U6zUrhwErjoq5h27S4dxkJEXVvhbdwyPv9U+3OkUuNLkZT4h2Kfrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axios": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001700", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", + "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/cliui/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/cliui/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/cliui/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/cliui/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/cliui/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/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concurrently": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz", + "integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "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/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/datatables.net": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.2.2.tgz", + "integrity": "sha512-gfODIKE3gpgbVeZy2QGj2Dq9roO6hy00S+k1knklrqlMyAMrh1wt0Q6ryBUM7gU96U77ysbq8dYhxFdmcC/oPQ==", + "license": "MIT", + "dependencies": { + "jquery": ">=1.7" + } + }, + "node_modules/datatables.net-buttons": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/datatables.net-buttons/-/datatables.net-buttons-3.2.2.tgz", + "integrity": "sha512-+aLTbkbksNmyGpK+8KXbpwYKXYOXvZQR2ySA/8oOQeJU53Xw/67cOHowenEr2d43/RLaz+I0zvV/1Yn+jMRiDw==", + "license": "MIT", + "dependencies": { + "datatables.net": "^2", + "jquery": ">=1.7" + } + }, + "node_modules/datatables.net-dt": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/datatables.net-dt/-/datatables.net-dt-2.2.2.tgz", + "integrity": "sha512-Qfe9g/E3yAPTHoDASc1J0r5Yx++3Y3VdcEOFCupvfGJ8LhRrreebUC70UYEzO8vPOKnkutoRanW/VBMRIbXKXA==", + "license": "MIT", + "dependencies": { + "datatables.net": "2.2.2", + "jquery": ">=1.7" + } + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "license": "MIT", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/diff-dom": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/diff-dom/-/diff-dom-5.1.4.tgz", + "integrity": "sha512-TSEaVdVGictY1KHg7VpVw2nuM02YKC9C8/qBkGiCnkiAybVbu1zQTMj2/dnVLRO7Z62UsqzHGpXweiOj5/jaZg==", + "license": "LGPL-3.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.102", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.102.tgz", + "integrity": "sha512-eHhqaja8tE/FNpIiBrvBjFV/SSKpyWHLvxuR9dPTdo+3V9ppdLmFB7ZZQ98qNovcngPLYIz0oOBF9P0FfZef5Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" + } + }, + "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/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flowbite": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/flowbite/-/flowbite-2.5.2.tgz", + "integrity": "sha512-kwFD3n8/YW4EG8GlY3Od9IoKND97kitO+/ejISHSqpn3vw2i5K/+ZI8Jm2V+KC4fGdnfi0XZ+TzYqQb4Q1LshA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@popperjs/core": "^2.9.3", + "flowbite-datepicker": "^1.3.0", + "mini-svg-data-uri": "^1.4.3" + } + }, + "node_modules/flowbite-datepicker": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/flowbite-datepicker/-/flowbite-datepicker-1.3.2.tgz", + "integrity": "sha512-6Nfm0MCVX3mpaR7YSCjmEO2GO8CDt6CX8ZpQnGdeu03WUCWtEPQ/uy0PUiNtIJjJZWnX0Cm3H55MOhbD1g+E/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/plugin-node-resolve": "^15.2.3", + "flowbite": "^2.0.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gsap": { + "version": "3.12.7", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.7.tgz", + "integrity": "sha512-V4GsyVamhmKefvcAKaoy0h6si0xX7ogwBoBSs2CTJwt7luW0oZzC0LhdkyuKV8PJAXr7Yaj8pMjCKD4GJ+eEMg==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license. Club GSAP members get more: https://gsap.com/licensing/. Why GreenSock doesn't employ an MIT license: https://gsap.com/why-license/" + }, + "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/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jpeg-exif": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", + "integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==", + "license": "MIT" + }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", + "license": "MIT" + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/laravel-echo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/laravel-echo/-/laravel-echo-2.0.2.tgz", + "integrity": "sha512-Ciai6hA7r35MFqNRb8G034cvm9WiveSTFQQKRGJhWtZGbng7C8BBa5QvqDxk/Mw5GeJ+q19jrEwQhf7r1b1lcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/laravel-vite-plugin": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.2.0.tgz", + "integrity": "sha512-R0pJ+IcTVeqEMoKz/B2Ij57QVq3sFTABiFmb06gAwFdivbOgsUtuhX6N2MGLEArajrS3U5JbberzwOe7uXHMHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "vite-plugin-full-reload": "^1.1.0" + }, + "bin": { + "clean-orphaned-assets": "bin/clean.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0" + } + }, + "node_modules/lenis": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/lenis/-/lenis-1.1.20.tgz", + "integrity": "sha512-UE64mlJEN4W8DWP2tfHzPzygk94Q2BhjzeG/YHWoyeMJ2Fd6XziBCNN+IvHwmB4GkmKIYxgr0+TvRYTSAMTMFA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/darkroomengineering" + }, + "peerDependencies": { + "react": ">=17.0.0", + "vue": ">=3.0.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "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/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/pdfmake": { + "version": "0.2.18", + "resolved": "https://registry.npmjs.org/pdfmake/-/pdfmake-0.2.18.tgz", + "integrity": "sha512-Fe+GnMS8EVZu5rci/CDaQ+xmUoHvx8P+rvIlrwSYM6A5c7Aik8G6lpJbddhjBE2jXGjv6WcUCFCB06uZbjxkMw==", + "license": "MIT", + "dependencies": { + "@foliojs-fork/linebreak": "^1.1.2", + "@foliojs-fork/pdfkit": "^0.15.3", + "iconv-lite": "^0.6.3", + "xmldoc": "^1.3.0" + }, + "engines": { + "node": ">=18" + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/png-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz", + "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==" + }, + "node_modules/postcss": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", + "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", + "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.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/preline": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/preline/-/preline-2.7.0.tgz", + "integrity": "sha512-xMuMVZ7aftBT1/5/3Rb48i/t+LWQfsUjBX7bls4peqS6h5OZTbIgz0N6eMDzuDlOZF3DiSWLehl00oGlAkvovw==", + "license": "Licensed under MIT and Preline UI Fair Use License", + "dependencies": { + "@popperjs/core": "^2.11.2" + } + }, + "node_modules/prettier": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", + "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/pusher-js": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/pusher-js/-/pusher-js-8.4.0.tgz", + "integrity": "sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "tweetnacl": "^1.0.3" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.34.8", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", + "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.34.8", + "@rollup/rollup-android-arm64": "4.34.8", + "@rollup/rollup-darwin-arm64": "4.34.8", + "@rollup/rollup-darwin-x64": "4.34.8", + "@rollup/rollup-freebsd-arm64": "4.34.8", + "@rollup/rollup-freebsd-x64": "4.34.8", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", + "@rollup/rollup-linux-arm-musleabihf": "4.34.8", + "@rollup/rollup-linux-arm64-gnu": "4.34.8", + "@rollup/rollup-linux-arm64-musl": "4.34.8", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", + "@rollup/rollup-linux-riscv64-gnu": "4.34.8", + "@rollup/rollup-linux-s390x-gnu": "4.34.8", + "@rollup/rollup-linux-x64-gnu": "4.34.8", + "@rollup/rollup-linux-x64-musl": "4.34.8", + "@rollup/rollup-win32-arm64-msvc": "4.34.8", + "@rollup/rollup-win32-ia32-msvc": "4.34.8", + "@rollup/rollup-win32-x64-msvc": "4.34.8", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", + "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-datatables": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/simple-datatables/-/simple-datatables-9.2.2.tgz", + "integrity": "sha512-FgGJIblxM3aR+VdueglTHvZuN3R2D/q1HM272G3EcNVeUb+b9J8aWdtxoPZtKoUqmOdxc7IYWrM1HW+mi2Oq8A==", + "license": "LGPL-3.0", + "dependencies": { + "dayjs": "^1.11.10", + "diff-dom": "^5.1.3" + } + }, + "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_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "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/string-width-cjs/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/string-width-cjs/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/string-width-cjs/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/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "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/strip-ansi-cjs/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/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swiper": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.2.4.tgz", + "integrity": "sha512-DTtglrsFfMYytid+oNy4QI3t2N2+XhhwSYbnyOhlwBmvY8Bkoj3ombK1/b80w8vDpQ+Lqlnbm+0737+i32MrcA==", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "license": "MIT", + "engines": { + "node": ">= 4.7.0" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "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/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "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/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.1.tgz", + "integrity": "sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.24.2", + "postcss": "^8.5.2", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "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.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi-cjs/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/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/xmldoc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-1.3.0.tgz", + "integrity": "sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==", + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + } + }, + "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/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "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" + } + }, + "node_modules/yargs/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/yargs/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/yargs/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/yargs/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" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..acc1ee2 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "devDependencies": { + "@tailwindcss/forms": "^0.5.2", + "alpinejs": "^3.4.2", + "autoprefixer": "^10.4.20", + "axios": "^1.7.4", + "concurrently": "^9.0.1", + "flowbite": "^2.5.2", + "laravel-echo": "^2.0.2", + "laravel-vite-plugin": "^1.2.0", + "postcss": "^8.4.49", + "prettier": "^3.5.2", + "pusher-js": "^8.4.0", + "tailwindcss": "^3.4.15", + "vite": "^6.1.0" + }, + "dependencies": { + "@popperjs/core": "^2.11.8", + "@preline/accordion": "^2.5.2", + "@preline/datatable": "^2.5.2", + "@preline/dropdown": "^2.5.2", + "@preline/layout-splitter": "^2.5.2", + "@preline/stepper": "^2.5.2", + "@preline/tooltip": "^2.5.2", + "datatables.net-buttons": "^3.2.2", + "datatables.net-dt": "^2.2.2", + "gsap": "^3.12.7", + "jquery": "^3.7.1", + "jszip": "^3.10.1", + "lenis": "^1.1.20", + "pdfmake": "^0.2.18", + "preline": "^2.7.0", + "simple-datatables": "^9.2.1", + "swiper": "^11.2.3" + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..506b9a3 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,33 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..49c0612 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..3aec5e2 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # 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/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..e69de29 diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..947d989 --- /dev/null +++ b/public/index.php @@ -0,0 +1,17 @@ +handleRequest(Request::capture()); diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/public/static/image/fyi.png b/public/static/image/fyi.png new file mode 100644 index 0000000..0698678 Binary files /dev/null and b/public/static/image/fyi.png differ diff --git a/public/static/image/hero-section.png b/public/static/image/hero-section.png new file mode 100644 index 0000000..307180c Binary files /dev/null and b/public/static/image/hero-section.png differ diff --git a/public/static/image/hero.png b/public/static/image/hero.png new file mode 100644 index 0000000..40ec77b Binary files /dev/null and b/public/static/image/hero.png differ diff --git a/public/static/image/logo_polije.png b/public/static/image/logo_polije.png new file mode 100644 index 0000000..5ec5771 Binary files /dev/null and b/public/static/image/logo_polije.png differ diff --git a/public/static/image/profile-placeholder.jpg b/public/static/image/profile-placeholder.jpg new file mode 100644 index 0000000..4defc01 Binary files /dev/null and b/public/static/image/profile-placeholder.jpg differ diff --git a/resources/css/app.css b/resources/css/app.css new file mode 100644 index 0000000..20614e3 --- /dev/null +++ b/resources/css/app.css @@ -0,0 +1,217 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +html, +body { + overflow: hidden !important; + scroll-behavior: smooth; +} + +.dt-layout-row:has(.dt-search), +.dt-layout-row:has(.dt-length), +.dt-layout-row:has(.dt-paging) { + display: none !important; +} + +.dt-empty { + height: 125px; + justify-content: center; + align-items: center; + text-align: center; +} + +.scroll-tab::-webkit-scrollbar { + display: none !important; +} + +.scroll-tab { + -ms-overflow-style: none !important; + scrollbar-width: none !important; +} + +.swiper-slide { + width: 248px !important; + transition: + all 1s ease, + opacity 0.5s ease !important; +} + +.swiper-slide img { + transition: all 1s ease !important; +} + +.swiper-slide-active { + width: 472px !important; + /* height: 300px !important; */ + transform: scale(1.2); + opacity: 1; + z-index: 2; + cursor: pointer; +} + +.swiper-slide img { + transition: all 500ms ease-in-out !important; + opacity: 0.5; +} + +.swiper-slide-active #profile-card { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; +} + +.swiper-slide-active img { + width: 100px !important; + height: 100px !important; + opacity: 1; +} + +.swiper-slide-active .text-content-swipper { + margin-top: 1.5rem; + display: block; + opacity: 1; +} + +.swiper-pagination { + position: relative !important; + bottom: 0; + background: transparent !important; + display: flex; + align-items: center; + justify-content: center; + width: auto; +} + +#navbutton:hover .menu-line:nth-child(1) { + opacity: 0; + transform: translateY(5px); +} + +#navbutton:hover .menu-line:nth-child(2) { + transform: translateY(-5px) scale(1.2); +} + +#navbutton.open .menu-line:nth-child(1) { + opacity: 1; + transform: translateY(4px) translateX(-3px) rotate(38deg); +} + +#navbutton.open .menu-line:nth-child(2) { + transform: translateY(-3px) translateX(-3px) rotate(-38deg); +} + +.menu-wrap.menu-wrap--open { + pointer-events: auto; + opacity: 1; +} + +.frame--menu-open #navbutton { + display: flex !important; + visibility: visible !important; + opacity: 1 !important; + z-index: 100 !important; +} + +.frame--menu-open { + backdrop-filter: blur(1.5rem); +} + +.frame--menu-open #navheader-right { + position: relative; + opacity: 1; + z-index: 100; + will-change: transform, opacity; +} + +.menu-wrap.menu-wrap--open { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 50; +} + +.hover-underline { + color: #ffffff; + position: relative; + display: inline-block; +} + +.hover-underline::after { + content: ""; + position: absolute; + width: 100%; + height: 2px; + background: #ffffff; + bottom: -5px; + left: 0; + transform: scaleX(-0); + transform-origin: left; + transition: transform 0.4s ease-out; +} + +.hover-underline:hover::after { + transform: scaleX(1); +} + +.hover-underline-expertise { + color: #000; + position: relative; +} + +.hover-underline-expertise::after { + content: ""; + position: absolute; + width: 100%; + height: 2px; + color: transparent; + background: #020617; + bottom: -5px; + left: 0; + transform: scaleX(-0); + transform-origin: left; + transition: transform 0.4s ease-out; +} + +.hover-underline-expertise:hover::after { + transform: scaleX(1); +} + +@media (max-width: 1024px) { + .swiper-slide { + width: 220px !important; + } + + .swiper-slide-active { + width: 360px !important; + } +} + +@media (max-width: 768px) { + .swiper-slide { + width: 180px !important; + } + + .swiper-slide-active { + width: 280px !important; + } +} + +@media (max-width: 480px) { + .swiper-slide { + width: 150px !important; + } + + .swiper-slide-active { + width: 250px !important; + } + + .swiper-slide-active img { + width: 80px !important; + height: 80px !important; + } +} diff --git a/resources/js/animated/navigation.js b/resources/js/animated/navigation.js new file mode 100644 index 0000000..5c3ecfe --- /dev/null +++ b/resources/js/animated/navigation.js @@ -0,0 +1,169 @@ +import gsap from "gsap"; + +// DOM elements +const DOM = { + frame: document.querySelector(".frame"), + overlayPath: document.querySelector(".overlay__path"), + menuWrap: document.querySelector(".menu-wrap"), + menuItems: document.querySelectorAll(".menu__item"), + openMenuCtrl: document.querySelector("button#navbutton"), + closeMenuCtrl: document.querySelector(".button#navbutton.open"), + menuToggle: document.querySelector("#navbutton"), + brand: document.querySelector("#brand"), + navLink: document.querySelectorAll("#nav-link"), +}; + +let isAnimating = false; + +const openMenu = () => { + if (isAnimating) return; + isAnimating = true; + DOM.menuToggle.classList.add("open"); + + gsap.timeline({ + onComplete: () => { + isAnimating = false; + }, + }) + .set(DOM.overlayPath, { + attr: { d: "M 0 100 V 100 Q 50 100 100 100 V 100 z" }, + }) + .to( + DOM.overlayPath, + { + duration: 0.8, + ease: "power4.in", + attr: { d: "M 0 100 V 50 Q 50 0 100 50 V 100 z" }, + }, + 0 + ) + .to(DOM.overlayPath, { + duration: 0.3, + ease: "power2", + attr: { d: "M 0 100 V 0 Q 50 0 100 0 V 100 z" }, + onComplete: () => { + DOM.frame.classList.add("frame--menu-open"); + DOM.menuWrap.classList.add("menu-wrap--open"); + }, + }) + .set(DOM.menuItems, { opacity: 0 }) + .set(DOM.overlayPath, { + attr: { d: "M 0 0 V 100 Q 50 100 100 100 V 0 z" }, + }) + .to(DOM.overlayPath, { + duration: 0.3, + ease: "power2.in", + attr: { d: "M 0 0 V 50 Q 50 0 100 50 V 0 z" }, + }) + .to(DOM.overlayPath, { + duration: 0.8, + ease: "power4", + attr: { d: "M 0 0 V 0 Q 50 0 100 0 V 0 z" }, + }) + .to( + DOM.menuItems, + { + duration: 1.1, + ease: "power4", + startAt: { y: 150 }, + y: 0, + opacity: 1, + stagger: 0.05, + }, + ">-=1.1" + ); +}; + +const closeMenu = () => { + if (isAnimating) return; + isAnimating = true; + DOM.menuToggle.classList.remove("open"); + + gsap.timeline({ + onComplete: () => { + isAnimating = false; + }, + }) + .set(DOM.overlayPath, { attr: { d: "M 0 0 V 0 Q 50 0 100 0 V 0 z" } }) + .to( + DOM.overlayPath, + { + duration: 0.8, + ease: "power4.in", + attr: { d: "M 0 0 V 50 Q 50 100 100 50 V 0 z" }, + }, + 0 + ) + .to(DOM.overlayPath, { + duration: 0.3, + ease: "power2", + attr: { d: "M 0 0 V 100 Q 50 100 100 100 V 0 z" }, + onComplete: () => { + DOM.frame.classList.remove("frame--menu-open"); + DOM.menuWrap.classList.remove("menu-wrap--open"); + }, + }) + .set(DOM.overlayPath, { + attr: { d: "M 0 100 V 0 Q 50 0 100 0 V 100 z" }, + }) + .to(DOM.overlayPath, { + duration: 0.3, + ease: "power2.in", + attr: { d: "M 0 100 V 50 Q 50 100 100 50 V 100 z" }, + }) + .to(DOM.overlayPath, { + duration: 0.8, + ease: "power4", + attr: { d: "M 0 100 V 100 Q 50 100 100 100 V 100 z" }, + }) + .to( + DOM.menuItems, + { + duration: 0.8, + ease: "power2.in", + y: 100, + opacity: 0, + stagger: -0.05, + }, + 0 + ); +}; + +const handleScroll = () => { + const contentSection = document.querySelector("#content-report"); + const isPendahuluan = window.location.hash === "#pendahuluan"; + + if (!contentSection) return; + const contentTop = contentSection.offsetTop; + const contentHeight = contentSection.offsetHeight; + const scrollPosition = window.scrollY; + const idInContent = scrollPosition >= contentTop && scrollPosition <= contentTop + contentHeight; + + DOM.navLink.forEach((link) => { + link.classList.toggle("text-white", !idInContent && !isPendahuluan); + link.classList.toggle("text-neutral-950", idInContent || isPendahuluan); + }); + + DOM.brand.classList.toggle("text-white", !idInContent && !isPendahuluan); + DOM.brand.classList.toggle("text-neutral-950", idInContent || isPendahuluan); +}; + +const initScrollEvents = () => { + window.addEventListener("scroll", () => requestAnimationFrame(handleScroll)); + window.addEventListener("hashchange", handleScroll); + handleScroll(); +}; + +export function initNavigation() { + if (!DOM.menuToggle) return; + + DOM.menuToggle.addEventListener("click", function () { + if (DOM.frame.classList.contains("frame--menu-open")) { + closeMenu(); + } else { + openMenu(); + } + }); + + initScrollEvents(); +} diff --git a/resources/js/app.js b/resources/js/app.js new file mode 100644 index 0000000..06a845d --- /dev/null +++ b/resources/js/app.js @@ -0,0 +1,209 @@ +import "./bootstrap"; +import "flowbite"; +import "preline"; +import Swiper from "swiper"; +import { Navigation, Pagination } from "swiper/modules"; +import "swiper/css"; +import "swiper/css/navigation"; +import "swiper/css/pagination"; +import Lenis from "lenis"; +import Alpine from "alpinejs"; +import HSStepper from "@preline/stepper"; +import HSDataTable from "@preline/datatable"; +import { initNavigation } from "./animated/navigation"; +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; + +gsap.registerPlugin(ScrollTrigger); + +const lenis = new Lenis(); + +lenis.on("scroll", ScrollTrigger.update); + +gsap.ticker.add((time) => { + lenis.raf(time * 1000); +}); + +gsap.ticker.lagSmoothing(0); + +window.Alpine = Alpine; + +Alpine.start(); + +// if ( +// document.getElementById("pagination-table") && +// typeof simpleDatatables.DataTable !== "undefined" +// ) { +// const dataTable = new simpleDatatables.DataTable("#pagination-table", { +// paging: true, +// perPage: 10, +// perPageSelect: [10, 15, 20, 25], +// sortable: true, +// firstLast: true, +// nextPrev: true, +// searchable: true, +// sensitivity: "base", +// searchQuerySeparator: " ", +// }); +// } + +function formatPresentase(input) { + let value = parseInt(input.value.replace(/[^0-9]/g, ""), 10); + + if (isNaN(value) || value < 0) { + value = 0; + } else if (value > 100) { + value = 100; + } + + input.value = value + "%"; +} + +window.formatPresentase = formatPresentase; + +let selectedCount = 0; + +function limitCheckboxes(checkbox) { + const checkboxes = document.querySelectorAll(".checkbox"); + + if (checkbox.checked) { + selectedCount++; + } else { + selectedCount--; + } + + if (selectedCount >= 3) { + checkboxes.forEach(function (cb) { + if (!cb.checked) { + cb.disabled = true; + } + }); + } else { + checkboxes.forEach(function (cb) { + cb.disabled = false; + }); + } +} + +// window.addEventListener("load", () => { +// (function () { +// const { dataTable } = new HSDataTable("#hs-datatable-with-export"); +// const buttons = document.querySelectorAll( +// "#hs-dropdown-datatable-with-export .hs-dropdown-menu button", +// ); + +// buttons.forEach((btn) => { +// const type = btn.getAttribute("data-hs-datatable-action-type"); + +// btn.addEventListener("click", () => +// dataTable.button(`.buttons-${type}`).trigger(), +// ); +// }); +// })(); +// }); + +document.addEventListener("DOMContentLoaded", function () { + const checkboxes = document.querySelectorAll(".checkbox"); + + checkboxes.forEach(function (checkbox) { + checkbox.addEventListener("click", function () { + limitCheckboxes(checkbox); + }); + }); + + const navbar = document.querySelector(".frame"); + + window.addEventListener("scroll", function () { + if (window.scrollY > 50) { + navbar.classList.add("shadow-md"); + } else { + navbar.classList.remove("shadow-md"); + } + }); + + new Swiper(".slider-testimonials", { + modules: [Navigation, Pagination], + centeredSlides: true, + centeredSlidesBounds: false, + effect: "slide", + lazyLoadingInPrevNext: true, + preloadImages: true, + freeMode: true, + loop: true, + slidesPerView: 3, + slidesPerGroup: 1, + spaceBetween: 10, + breakpoints: { + 320: { + slidesPerView: 1.6, + spaceBetween: 10, + }, + 370: { + slidesPerView: 1.8, + spaceBetween: 15, + }, + 400: { + slidesPerView: 2, + spaceBetween: 0, + }, + 450: { + slidesPerView: 2.3, + spaceBetween: 15, + }, + 500: { + slidesPerView: 2.5, + spaceBetween: 15, + }, + 550: { + slidesPerView: 2.8, + spaceBetween: 15, + }, + 600: { + slidesPerView: 3, + spaceBetween: 20, + }, + 650: { + slidesPerView: 3.2, + }, + 700: { + slidesPerView: 3.5, + }, + 750: { + slidesPerView: 3.7, + }, + 800: { + slidesPerView: 4, + }, + 900: { + slidesPerView: 4.2, + }, + 940: { + slidesPerView: 4.5, + }, + 1024: { + slidesPerView: 4, + spaceBetween: 50, + }, + }, + speed: 800, + pagination: { + el: ".swiper-pagination", + type: "progressbar", + renderProgressbar: function (progressbarFillClass) { + return ( + '' + ); + }, + }, + navigation: { + nextEl: "#button-next", + prevEl: "#button-prev", + }, + }); + + window.HSStaticMethods.autoInit(["select"]); + + initNavigation(); +}); diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js new file mode 100644 index 0000000..a8f152f --- /dev/null +++ b/resources/js/bootstrap.js @@ -0,0 +1,12 @@ +import axios from 'axios'; +window.axios = axios; + +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; + +/** + * Echo exposes an expressive API for subscribing to channels and listening + * for events that are broadcast by Laravel. Echo and event broadcasting + * allow your team to quickly build robust real-time web applications. + */ + +// import './echo'; diff --git a/resources/js/echo.js b/resources/js/echo.js new file mode 100644 index 0000000..b314315 --- /dev/null +++ b/resources/js/echo.js @@ -0,0 +1,14 @@ +// import Echo from 'laravel-echo'; + +// import Pusher from 'pusher-js'; +// window.Pusher = Pusher; + +// window.Echo = new Echo({ +// broadcaster: 'reverb', +// key: import.meta.env.VITE_REVERB_APP_KEY, +// wsHost: import.meta.env.VITE_REVERB_HOST, +// wsPort: import.meta.env.VITE_REVERB_PORT ?? 80, +// wssPort: import.meta.env.VITE_REVERB_PORT ?? 443, +// forceTLS: (import.meta.env.VITE_REVERB_SCHEME ?? 'https') === 'https', +// enabledTransports: ['ws', 'wss'], +// }); diff --git a/resources/views/components/application-logo.blade.php b/resources/views/components/application-logo.blade.php new file mode 100644 index 0000000..b0b06de --- /dev/null +++ b/resources/views/components/application-logo.blade.php @@ -0,0 +1 @@ +logo_instansi diff --git a/resources/views/components/auth-session-error.blade.php b/resources/views/components/auth-session-error.blade.php new file mode 100644 index 0000000..e3607b6 --- /dev/null +++ b/resources/views/components/auth-session-error.blade.php @@ -0,0 +1,23 @@ +@props(['status']) + +@if ($status) +
merge(['class' => 'flex items-center p-4 mb-4 text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400']) }} + role="alert" id="alert-status"> + +
{{ $status }}
+ +
+@endif diff --git a/resources/views/components/auth-session-status.blade.php b/resources/views/components/auth-session-status.blade.php new file mode 100644 index 0000000..4b8bbb3 --- /dev/null +++ b/resources/views/components/auth-session-status.blade.php @@ -0,0 +1,23 @@ +@props(['status']) + +@if ($status) +
merge(['class' => 'flex items-center p-4 mb-4 text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400']) }} + role="alert" id="alert-status"> + +
{{ $status }}
+ +
+@endif diff --git a/resources/views/components/danger-button.blade.php b/resources/views/components/danger-button.blade.php new file mode 100644 index 0000000..d7417b2 --- /dev/null +++ b/resources/views/components/danger-button.blade.php @@ -0,0 +1,3 @@ + diff --git a/resources/views/components/dropdown-link.blade.php b/resources/views/components/dropdown-link.blade.php new file mode 100644 index 0000000..6d5279d --- /dev/null +++ b/resources/views/components/dropdown-link.blade.php @@ -0,0 +1 @@ +merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-800 transition duration-150 ease-in-out']) }}>{{ $slot }} diff --git a/resources/views/components/dropdown.blade.php b/resources/views/components/dropdown.blade.php new file mode 100644 index 0000000..e4106a4 --- /dev/null +++ b/resources/views/components/dropdown.blade.php @@ -0,0 +1,35 @@ +@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white dark:bg-gray-700']) + +@php +$alignmentClasses = match ($align) { + 'left' => 'ltr:origin-top-left rtl:origin-top-right start-0', + 'top' => 'origin-top', + default => 'ltr:origin-top-right rtl:origin-top-left end-0', +}; + +$width = match ($width) { + '48' => 'w-48', + default => $width, +}; +@endphp + +
+
+ {{ $trigger }} +
+ + +
diff --git a/resources/views/components/input-error.blade.php b/resources/views/components/input-error.blade.php new file mode 100644 index 0000000..ad95f6b --- /dev/null +++ b/resources/views/components/input-error.blade.php @@ -0,0 +1,9 @@ +@props(['messages']) + +@if ($messages) +
    merge(['class' => 'text-sm text-red-600 dark:text-red-400 space-y-1']) }}> + @foreach ((array) $messages as $message) +
  • {{ $message }}
  • + @endforeach +
+@endif diff --git a/resources/views/components/input-label.blade.php b/resources/views/components/input-label.blade.php new file mode 100644 index 0000000..e93b059 --- /dev/null +++ b/resources/views/components/input-label.blade.php @@ -0,0 +1,5 @@ +@props(['value']) + + diff --git a/resources/views/components/modal.blade.php b/resources/views/components/modal.blade.php new file mode 100644 index 0000000..384662a --- /dev/null +++ b/resources/views/components/modal.blade.php @@ -0,0 +1,78 @@ +@props([ + 'name', + 'show' => false, + 'maxWidth' => '2xl' +]) + +@php +$maxWidth = [ + 'sm' => 'sm:max-w-sm', + 'md' => 'sm:max-w-md', + 'lg' => 'sm:max-w-lg', + 'xl' => 'sm:max-w-xl', + '2xl' => 'sm:max-w-2xl', +][$maxWidth]; +@endphp + +
+
+
+
+ +
+ {{ $slot }} +
+
diff --git a/resources/views/components/nav-item-dropdown.blade.php b/resources/views/components/nav-item-dropdown.blade.php new file mode 100644 index 0000000..fc4ceb3 --- /dev/null +++ b/resources/views/components/nav-item-dropdown.blade.php @@ -0,0 +1,33 @@ +@props(['hrefSystem', 'hrefAccount', 'title', 'icon', 'active' => false]) + +
  • + + + +
  • diff --git a/resources/views/components/nav-item.blade.php b/resources/views/components/nav-item.blade.php new file mode 100644 index 0000000..69dd7dc --- /dev/null +++ b/resources/views/components/nav-item.blade.php @@ -0,0 +1,9 @@ +@props(['href', 'title', 'icon', 'active' => false]) + +
  • + + {!! $icon !!} + {{ $title }} + +
  • diff --git a/resources/views/components/nav-link.blade.php b/resources/views/components/nav-link.blade.php new file mode 100644 index 0000000..37bad55 --- /dev/null +++ b/resources/views/components/nav-link.blade.php @@ -0,0 +1,11 @@ +@props(['active']) + +@php +$classes = ($active ?? false) + ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 dark:border-indigo-600 text-sm font-medium leading-5 text-gray-900 dark:text-gray-100 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out' + : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 hover:border-gray-300 dark:hover:border-gray-700 focus:outline-none focus:text-gray-700 dark:focus:text-gray-300 focus:border-gray-300 dark:focus:border-gray-700 transition duration-150 ease-in-out'; +@endphp + +merge(['class' => $classes]) }}> + {{ $slot }} + diff --git a/resources/views/components/primary-button.blade.php b/resources/views/components/primary-button.blade.php new file mode 100644 index 0000000..98aaad7 --- /dev/null +++ b/resources/views/components/primary-button.blade.php @@ -0,0 +1,4 @@ + diff --git a/resources/views/components/responsive-nav-link.blade.php b/resources/views/components/responsive-nav-link.blade.php new file mode 100644 index 0000000..98b55d1 --- /dev/null +++ b/resources/views/components/responsive-nav-link.blade.php @@ -0,0 +1,11 @@ +@props(['active']) + +@php +$classes = ($active ?? false) + ? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 dark:border-indigo-600 text-start text-base font-medium text-indigo-700 dark:text-indigo-300 bg-indigo-50 dark:bg-indigo-900/50 focus:outline-none focus:text-indigo-800 dark:focus:text-indigo-200 focus:bg-indigo-100 dark:focus:bg-indigo-900 focus:border-indigo-700 dark:focus:border-indigo-300 transition duration-150 ease-in-out' + : 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 hover:bg-gray-50 dark:hover:bg-gray-700 hover:border-gray-300 dark:hover:border-gray-600 focus:outline-none focus:text-gray-800 dark:focus:text-gray-200 focus:bg-gray-50 dark:focus:bg-gray-700 focus:border-gray-300 dark:focus:border-gray-600 transition duration-150 ease-in-out'; +@endphp + +merge(['class' => $classes]) }}> + {{ $slot }} + diff --git a/resources/views/components/secondary-button.blade.php b/resources/views/components/secondary-button.blade.php new file mode 100644 index 0000000..33e4538 --- /dev/null +++ b/resources/views/components/secondary-button.blade.php @@ -0,0 +1,4 @@ + diff --git a/resources/views/components/text-input.blade.php b/resources/views/components/text-input.blade.php new file mode 100644 index 0000000..7fa444e --- /dev/null +++ b/resources/views/components/text-input.blade.php @@ -0,0 +1,4 @@ +@props(['disabled' => false]) + +merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-primary dark:focus:border-primary focus:ring-primary dark:focus:ring-primary rounded-md shadow-sm']) }}> diff --git a/resources/views/errors/401.blade.php b/resources/views/errors/401.blade.php new file mode 100644 index 0000000..5c586db --- /dev/null +++ b/resources/views/errors/401.blade.php @@ -0,0 +1,5 @@ +@extends('errors::minimal') + +@section('title', __('Unauthorized')) +@section('code', '401') +@section('message', __('Unauthorized')) diff --git a/resources/views/errors/402.blade.php b/resources/views/errors/402.blade.php new file mode 100644 index 0000000..3bc23ef --- /dev/null +++ b/resources/views/errors/402.blade.php @@ -0,0 +1,5 @@ +@extends('errors::minimal') + +@section('title', __('Payment Required')) +@section('code', '402') +@section('message', __('Payment Required')) diff --git a/resources/views/errors/403.blade.php b/resources/views/errors/403.blade.php new file mode 100644 index 0000000..a5506f0 --- /dev/null +++ b/resources/views/errors/403.blade.php @@ -0,0 +1,5 @@ +@extends('errors::minimal') + +@section('title', __('Forbidden')) +@section('code', '403') +@section('message', __($exception->getMessage() ?: 'Forbidden')) diff --git a/resources/views/errors/404.blade.php b/resources/views/errors/404.blade.php new file mode 100644 index 0000000..34e133f --- /dev/null +++ b/resources/views/errors/404.blade.php @@ -0,0 +1,35 @@ +@extends('layouts.home') + +@section('content') +
    +
    + + +
    +
    +

    404

    +

    Oops, something went wrong.

    +

    Sorry, we couldn't find your page.

    + +
    +
    +
    +
    +@endsection diff --git a/resources/views/errors/419.blade.php b/resources/views/errors/419.blade.php new file mode 100644 index 0000000..b8a2ea7 --- /dev/null +++ b/resources/views/errors/419.blade.php @@ -0,0 +1,35 @@ +@extends('layouts.home') + +@section('content') +
    +
    + + +
    +
    +

    419

    +

    Oops, something went wrong

    +

    Please, Refresh this page again.

    + +
    +
    +
    +
    +@endsection diff --git a/resources/views/errors/429.blade.php b/resources/views/errors/429.blade.php new file mode 100644 index 0000000..f01b07b --- /dev/null +++ b/resources/views/errors/429.blade.php @@ -0,0 +1,5 @@ +@extends('errors::minimal') + +@section('title', __('Too Many Requests')) +@section('code', '429') +@section('message', __('Too Many Requests')) diff --git a/resources/views/errors/500.blade.php b/resources/views/errors/500.blade.php new file mode 100644 index 0000000..d9e95d9 --- /dev/null +++ b/resources/views/errors/500.blade.php @@ -0,0 +1,5 @@ +@extends('errors::minimal') + +@section('title', __('Server Error')) +@section('code', '500') +@section('message', __('Server Error')) diff --git a/resources/views/errors/503.blade.php b/resources/views/errors/503.blade.php new file mode 100644 index 0000000..c5a9dde --- /dev/null +++ b/resources/views/errors/503.blade.php @@ -0,0 +1,5 @@ +@extends('errors::minimal') + +@section('title', __('Service Unavailable')) +@section('code', '503') +@section('message', __('Service Unavailable')) diff --git a/resources/views/errors/layout.blade.php b/resources/views/errors/layout.blade.php new file mode 100644 index 0000000..019c2cd --- /dev/null +++ b/resources/views/errors/layout.blade.php @@ -0,0 +1,53 @@ + + + + + + + @yield('title') + + + + + +
    +
    +
    + @yield('message') +
    +
    +
    + + diff --git a/resources/views/errors/minimal.blade.php b/resources/views/errors/minimal.blade.php new file mode 100644 index 0000000..db69f25 --- /dev/null +++ b/resources/views/errors/minimal.blade.php @@ -0,0 +1,34 @@ + + + + + + + @yield('title') + + + + + + +
    +
    +
    +
    + @yield('code') +
    + +
    + @yield('message') +
    +
    +
    +
    + + diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php new file mode 100644 index 0000000..0dad80c --- /dev/null +++ b/resources/views/layouts/app.blade.php @@ -0,0 +1,120 @@ + + + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + {{-- jquery --}} + + + + + {{--
    --}} + + {{-- + +
    + + +
    +
    +
    + +
    +
    +
    +
    --}} + {{--
    --}} + + @include('layouts.navigation') + + +
    + +
    +
    + + + + + +
      +
    1. + + Home + + + + +
    2. + @yield('mobile-breadcrumb') +
    + +
    +
    + +
    + + + @include('layouts.sidebar') + + + +
    +
    + {{ $slot }} +
    +
    + + + + {{-- flowbite --}} + + + + + + + + {{-- + + + + + + --}} + + + + diff --git a/resources/views/layouts/guest.blade.php b/resources/views/layouts/guest.blade.php new file mode 100644 index 0000000..eb9f643 --- /dev/null +++ b/resources/views/layouts/guest.blade.php @@ -0,0 +1,38 @@ + + + + + + + + + {{ config('app.name', 'Laravel') }} + + + + + + + + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + + +
    + +
    +
    + + + +
    + {{ $slot }} +
    +
    + + + diff --git a/resources/views/layouts/home.blade.php b/resources/views/layouts/home.blade.php new file mode 100644 index 0000000..fd229a2 --- /dev/null +++ b/resources/views/layouts/home.blade.php @@ -0,0 +1,18 @@ + + + + + + @include('partials.metadata') + @include('partials.styles') + + + + + + @include('partials.content') + @include('partials.scripts') + + + + diff --git a/resources/views/layouts/navigation.blade.php b/resources/views/layouts/navigation.blade.php new file mode 100644 index 0000000..1b80535 --- /dev/null +++ b/resources/views/layouts/navigation.blade.php @@ -0,0 +1,163 @@ +{{-- --}} + +
    + +
    + diff --git a/resources/views/layouts/sidebar.blade.php b/resources/views/layouts/sidebar.blade.php new file mode 100644 index 0000000..2137cbc --- /dev/null +++ b/resources/views/layouts/sidebar.blade.php @@ -0,0 +1,330 @@ +{{-- --}} + + diff --git a/resources/views/pages/alternatif/create.blade.php b/resources/views/pages/alternatif/create.blade.php new file mode 100644 index 0000000..95d2808 --- /dev/null +++ b/resources/views/pages/alternatif/create.blade.php @@ -0,0 +1,145 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Alternatif') }} +
    +
      +
    1. + + + Home + + + + Data Alternatif + + +
    2. +
    3. + Tambah Data Alternatif +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Alternatif + + + + +
  • +
  • + Tambah Data +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Tambah Data Alternatif') }} +

    +
    +
    + +
    + @csrf + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/alternatif/edit.blade.php b/resources/views/pages/alternatif/edit.blade.php new file mode 100644 index 0000000..7574677 --- /dev/null +++ b/resources/views/pages/alternatif/edit.blade.php @@ -0,0 +1,147 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Alternatif') }} +
    +
      +
    1. + + + Home + + + + Data Alternatif + + +
    2. +
    3. + Edit Data Alternatif +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Alternatif + + + + +
  • +
  • + Tambah Data +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Edit Data Alternatif') }} +

    +
    +
    + +
    + @csrf + @method('PUT') + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/alternatif/index.blade.php b/resources/views/pages/alternatif/index.blade.php new file mode 100644 index 0000000..7cad9c5 --- /dev/null +++ b/resources/views/pages/alternatif/index.blade.php @@ -0,0 +1,434 @@ +@role('admin') + @section('Breadcrumb') +
    +
    + {{ __('Data Alternatif') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Data Alternatif +
    4. +
    +
    + @endsection + + @section('mobile-breadcrumb') +
  • + Alternatif +
  • + @endsection +@endrole + +@role('mahasiswa') + @section('Breadcrumb') +
    +
    + {{ __('Data Dosen') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Data Dosen +
    4. +
    +
    + @endsection + + @section('mobile-breadcrumb') +
  • + Dosen +
  • + @endsection +@endrole + + + +
    + +
    +
    + @role('admin') +

    + {{ __('Data Alternatif') }} +

    + @endrole + @role('mahasiswa') +

    + {{ __('Daftar Dosen') }} +

    + @endrole + + @role('admin') + + {{ __('Tambah Data') }} + + @endrole +
    +
    + + {{-- + + + + + + + + + + @foreach ($alternatif as $item) + + + + + + + @endforeach + +
    + + No + + + + + NIP + + + + @role('admin') + + Nama Alternatif + + + @endrole + @role('mahasiswa') + + Nama Dosen + + + @endrole + + + Tindakan + + +
    {{ $loop->iteration }}{{ $item->nip }}{{ $item->name }} + + + + + + +
    --}} +
    +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + @foreach ($alternatif as $item) + + + + + + + @endforeach + +
    +
    + No + + + + +
    +
    +
    + NIP + + + + +
    +
    +
    + Nama + + + + +
    +
    + Action
    + {{ $loop->iteration }} + {{ $item->nip }} + {{ $item->name }} +
    + + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    diff --git a/resources/views/pages/aspek/create.blade.php b/resources/views/pages/aspek/create.blade.php new file mode 100644 index 0000000..921d4a9 --- /dev/null +++ b/resources/views/pages/aspek/create.blade.php @@ -0,0 +1,162 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Aspek') }} +
    +
      +
    1. + + + Home + + + + Data Aspek + + +
    2. +
    3. + Tambah Data Aspek +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Aspek + + + + +
  • +
  • + Tambah Data +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Tambah Data Aspek') }} +

    +
    +
    + +
    + @csrf + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/aspek/edit.blade.php b/resources/views/pages/aspek/edit.blade.php new file mode 100644 index 0000000..99f0210 --- /dev/null +++ b/resources/views/pages/aspek/edit.blade.php @@ -0,0 +1,165 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Aspek') }} +
    +
      +
    1. + + + Home + + + + Data Aspek + + +
    2. +
    3. + Edit Data Aspek +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Aspek + + + + +
  • +
  • + Edit Data {{ $aspek->aspek_name }} +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Edit Data Aspek') }} +

    +
    +
    + +
    + @csrf + @method('PUT') + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/aspek/index.blade.php b/resources/views/pages/aspek/index.blade.php new file mode 100644 index 0000000..9f0c977 --- /dev/null +++ b/resources/views/pages/aspek/index.blade.php @@ -0,0 +1,312 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Aspek') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Data Aspek +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + Aspek +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Data Aspek') }} +

    + + {{ __('Tambah Data') }} + +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + @foreach ($aspek as $item) + + + + + + + + + @endforeach + +
    +
    + No + + + + +
    +
    +
    + Kode + + + + +
    +
    +
    + Aspek + + + + +
    +
    +
    + Persentase + + + + +
    +
    +
    + Keterangan + + + + +
    +
    + Action
    + {{ $loop->iteration }} + {{ $item->kode_aspek }} + {{ $item->aspek_name }} + {{ $item->persentase }}% + {{ $item->keterangan }} +
    + + + +
    +
    +
    + + +
    +
    +
    +
    + +
    + +
    diff --git a/resources/views/pages/aspek/show.blade.php b/resources/views/pages/aspek/show.blade.php new file mode 100644 index 0000000..9ee8f2e --- /dev/null +++ b/resources/views/pages/aspek/show.blade.php @@ -0,0 +1,133 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Aspek') }} +
    +
      +
    1. + + + Home + + + + Data Aspek + + +
    2. +
    3. + Lihat Detail Data Aspek +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Aspek + + + + +
  • +
  • + Lihat Detail Data {{ $aspek->aspek_name }} +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Lihat Detail Data Aspek') }} +

    +
    +
    + + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + + + + +
    +
    diff --git a/resources/views/pages/auth/confirm-password.blade.php b/resources/views/pages/auth/confirm-password.blade.php new file mode 100644 index 0000000..3cbbe08 --- /dev/null +++ b/resources/views/pages/auth/confirm-password.blade.php @@ -0,0 +1,27 @@ + +
    + {{ __('This is a secure area of the application. Please confirm your password before continuing.') }} +
    + +
    + @csrf + + +
    + + + + + +
    + +
    + + {{ __('Confirm') }} + +
    +
    +
    diff --git a/resources/views/pages/auth/forgot-password.blade.php b/resources/views/pages/auth/forgot-password.blade.php new file mode 100644 index 0000000..b38b742 --- /dev/null +++ b/resources/views/pages/auth/forgot-password.blade.php @@ -0,0 +1,30 @@ + + +
    +
    +

    {{ __('Forgot Password') }}

    +

    {{ __('Enter your email below to reset your account password') }}

    +
    +
    + + + + +
    + @csrf + + +
    + + + +
    + +
    + + {{ __('Send Link') }} + +
    +
    +
    diff --git a/resources/views/pages/auth/login.blade.php b/resources/views/pages/auth/login.blade.php new file mode 100644 index 0000000..4e69a7f --- /dev/null +++ b/resources/views/pages/auth/login.blade.php @@ -0,0 +1,72 @@ + + +
    +
    +

    {{ __('Login') }}

    +

    {{ __('Enter your email below to login to your account') }}

    +
    +
    + + + @if (session('error')) + + @else + + @endif + +
    + @csrf + + +
    + + + +
    + + +
    + + + + + +
    + +
    + {{-- @if (Route::has('password.request')) + + {{ __('Forgot Password?') }} + + @endif --}} +
    + + + +
    +

    {{ __('Dont Have Account?') }}

    + + {{ __('Register') }} + +
    +
    +
    diff --git a/resources/views/pages/auth/register.blade.php b/resources/views/pages/auth/register.blade.php new file mode 100644 index 0000000..7399a54 --- /dev/null +++ b/resources/views/pages/auth/register.blade.php @@ -0,0 +1,73 @@ + + +
    +
    +

    {{ __('Create Account') }}

    +

    {{ __('Enter your account below to register your new account') }}

    +
    +
    + +
    + @csrf + + +
    + + + +
    + + +
    + + + +
    + + +
    + + + + + +
    + + +
    + + + + + +
    + +
    + + {{ __('Register') }} + + + {{-- + + + + + {{ __('Register with Google') }} + --}} +
    + +
    +

    {{ __('Already Have Account?') }}

    + + {{ __('Login') }} + +
    +
    +
    diff --git a/resources/views/pages/auth/reset-password.blade.php b/resources/views/pages/auth/reset-password.blade.php new file mode 100644 index 0000000..a6494cc --- /dev/null +++ b/resources/views/pages/auth/reset-password.blade.php @@ -0,0 +1,39 @@ + +
    + @csrf + + + + + +
    + + + +
    + + +
    + + + +
    + + +
    + + + + + +
    + +
    + + {{ __('Reset Password') }} + +
    +
    +
    diff --git a/resources/views/pages/auth/verify-email.blade.php b/resources/views/pages/auth/verify-email.blade.php new file mode 100644 index 0000000..4e4222f --- /dev/null +++ b/resources/views/pages/auth/verify-email.blade.php @@ -0,0 +1,31 @@ + +
    + {{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }} +
    + + @if (session('status') == 'verification-link-sent') +
    + {{ __('A new verification link has been sent to the email address you provided during registration.') }} +
    + @endif + +
    +
    + @csrf + +
    + + {{ __('Resend Verification Email') }} + +
    +
    + +
    + @csrf + + +
    +
    +
    diff --git a/resources/views/pages/dashboard/index.blade.php b/resources/views/pages/dashboard/index.blade.php new file mode 100644 index 0000000..fb38222 --- /dev/null +++ b/resources/views/pages/dashboard/index.blade.php @@ -0,0 +1,253 @@ +@section('mobile-breadcrumb') +
  • + Hello, {{ Auth::user()->name }} +
  • +@endsection + + + +
    + +
    +
    +
    + + + + +
    + +
    +
    +

    + Dosen +

    +
    +
    +

    + {{ $jumlah_alternatif }} +

    + + + + + + + 12.5% + + +
    +
    +
    +
    + +
    +
    +
    + + + + +
    + +
    +
    +

    + Mahasiswa +

    +
    +
    +

    + {{ $jumlah_mahasiswa }} +

    + + + + + + + + 12.5% + + +
    +
    +
    +
    + +
    +
    +
    + + + + +
    + +
    +
    +

    + Pengajuan Judul +

    +
    +
    +

    + {{ $jumlah_pengajuan }} +

    + + + + + + + 12.5% + + +
    +
    +
    +
    + +
    +
    +
    + + + + +
    + +
    +
    +

    + Pengunjung Website +

    +
    +
    +

    + 72,540 +

    + + + + + + + 12.5% + + +
    +
    +
    +
    + +
    +
    +
    +
    +
    +

    Profile Matching

    +
    +
    +

    Profile matching merupakan proses membandingkan antara kompetensi + + individu ke dalam kompetensi jabatan sehingga dapat diketahui perbedaan + + kompetensinya (disebut juga GAP), semakin kecil GAP yang dihasilkan maka + + bobot nilainya semakin besar. Profile Matching menganggap bahwa terdapat + + tingkat predictor variables ideal yang harus dimiliki seseorang. Dalam hal ini + + tidak berarti tingkat minimal yang harus dipenuhi atau dilewati. Setelah + + diperoleh GAP pada masing-masing individu, setiap profil individu diberi + + bobot nilai sesuai dengan ketentuan. (Al Muhdi, 2024)

    +
    +
    +
    +
    +
    + +
    +

    Dosen Pembimbing

    +
    + + +
    + @php + $maxItems = 3; + @endphp + + @foreach ($alternatif->take($maxItems) as $item) + + Polije Logo + +
    + + {{ $item->name }} + + + {{ $item->nip }} + +
    +
    + @endforeach + + + @if ($alternatif->count() > $maxItems) + + View All + + @endif +
    +
    + +
    + +
    + +
    + +{{-- @section('scripts') + + +@endsection --}} diff --git a/resources/views/pages/home/index.blade.php b/resources/views/pages/home/index.blade.php new file mode 100644 index 0000000..908482b --- /dev/null +++ b/resources/views/pages/home/index.blade.php @@ -0,0 +1,711 @@ +@extends('layouts.home') + +@section('Home', 'title') + +@section('content') + + +
    +
    +
    +

    + Sistem Pendukung Keputusan
    + Pemilihan Dosen Pembimbing Skripsi +

    +

    + Sistem ini membantu mahasiswa D4-Teknik Informatika memilih dosen pembimbing skripsi dengan menggunakan + metode Profile Matching. +

    + +
    +
    + Mockup +
    +
    +
    + + + +
    +
    +
    +

    Rumusan Masalah

    +

    Rumusan masalah dari penulisan laporan + ini + adalah sebagai berikut

    +
    + + +
    +
    + + + + + + + + + +
    +

    Implementasi Metode

    +

    Bagaimana mengimplementasikan metode + Profile + Matching untuk sistem pendukung keputusan pemilihan dosen pembimbing skripsi?

    +
    + + +
    +
    + + + + +
    +

    Rancang Bangun Sistem

    +

    Bagaimana merancang dan membangun sistem + pendukung keputusan pemilihan dosen pembimbing skripsi?

    +
    + + +
    +
    + + + + + + + + +
    +

    Evaluasi Sistem

    +

    Bagaimana mengevaluasi sistem pendukung + keputusan dalam pemilihan dosen pembimbing skripsi menggunakan Profile Matching?

    +
    + +
    +
    +
    + +
    +
    +
    +
    +

    + Tujuan Penelitian +

    +

    + Penelitian ini bertujuan untuk mengembangkan sistem pendukung keputusan dalam pemilihan + dosen + pembimbing skripsi dengan metode Profile Matching. +

    +
    + + +
    + +
    + + + + + + + + +
    +

    + Kesesuaian Profile Matching dalam Pemilihan Dosen +

    +

    + Mengetahui tingkat kesesuaian metode Profile Matching dalam pemilihan dosen + pembimbing + skripsi +

    +
    +
    + + + +
    + + + + + + + + + + + + + + + + + +
    +

    + Hasil Sistem +

    +

    + Menghasilkan sistem pedukung keputusan pemilihan dosen pembimbing skripsi. +

    +
    +
    + + + +
    + + + + + + + + + + + + + + +
    +

    + Hasil Pengujian Sistem Profile Matching +

    +

    + Mengetahui hasil dari pengujian terhadap sistem pendukung keputusan menggunakan + metode + Profile Matching dalam pemilihan dosen pembimbing skripsi. +

    +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    + Manfaat Penelitian +

    +

    Manfaat dari penelitian ini + adalah sebagai berikut

    + + + + +
    + + +
    +
    + +
    +
    + Features Image +
    + + + + +
    + + + + + +
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +
    + +
    +
    + +
    +
    +
    +

    + Metode
    Profile + Matching

    + + +
    +

    + "Profile Matching merupakan proses membandingkan antara kompetensi individu ke dalam + kompetensi jabatan sehingga dapat diketahui perbedaan kompetensinya (disebut juga + GAP), + semakin kecil GAP yang dihasilkan maka bobot nilainya semakin besar. (Muhdi Al + Miftahurrahman, 2024)" +

    +
    + +

    + + Pelajari lebih lanjut + + + + +

    +
    +
    + + +
    + +
    +
    + +
    +

    + Proses perhitungan pada penggunaan metode Profile Matching didahului memberikan + value pada setiap variabel kriteria. + Berikut data yang telah ditentukan.
    + a. Kuota Bimbingan
    + b. Kepakaran atau Bidang Keahlian
    + c. Riset Penelitian
    + +

    +
    +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    + +
    + +
    +
    + +
    +
    +

    Pendapat Mahasiswa

    +

    Berikut pendapat mahasiswa + berdasarkan hasil pengisian kuisioner

    + +
    +
    + +
    + + +
    +
    +
    +
    + + + +
    +
    + + + +
    +
    +
    +
    +
    +
    +
    + +@endsection + +@section('scripts') + +@endsection diff --git a/resources/views/pages/home/kesimpulan.blade.php b/resources/views/pages/home/kesimpulan.blade.php new file mode 100644 index 0000000..34e133f --- /dev/null +++ b/resources/views/pages/home/kesimpulan.blade.php @@ -0,0 +1,35 @@ +@extends('layouts.home') + +@section('content') +
    +
    + + +
    +
    +

    404

    +

    Oops, something went wrong.

    +

    Sorry, we couldn't find your page.

    + +
    +
    +
    +
    +@endsection diff --git a/resources/views/pages/home/metode.blade.php b/resources/views/pages/home/metode.blade.php new file mode 100644 index 0000000..acba3d5 --- /dev/null +++ b/resources/views/pages/home/metode.blade.php @@ -0,0 +1,289 @@ +@extends('layouts.home') + +@section('content') +
    +
    + + + + +
    +
    +
    +

    + Sistem Pendukung Keputusan +

    + + +
    +

    + Metode Profile Matching +

    +
    + + +
    +

    Profile Matching adalah metode yang membandingkan kompetensi individu dengan standar penilaian untuk mengidentifikasi selisih (GAP). Semakin kecil GAP, semakin tinggi bobot nilai yang diperoleh. Metode ini mengasumsikan adanya tingkat kompetensi ideal yang tidak selalu berarti batas minimum. Setelah GAP dihitung, setiap individu diberi bobot nilai sesuai ketentuan.

    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    +
    +

    + Metode
    Profile + Matching

    + + +
    +

    + "Profile Matching merupakan proses membandingkan antara kompetensi individu ke dalam + kompetensi jabatan sehingga dapat diketahui perbedaan kompetensinya (disebut juga + GAP), + semakin kecil GAP yang dihasilkan maka bobot nilainya semakin besar. (Muhdi Al + Miftahurrahman, 2024)" +

    +
    + +

    + + Pelajari lebih lanjut + + + + +

    +
    +
    + + +
    + +
    +
    + +
    +

    + Proses perhitungan pada penggunaan metode Profile Matching didahului memberikan + value pada setiap variabel kriteria. + Berikut data yang telah ditentukan.
    + a. Kuota Bimbingan
    + b. Kepakaran atau Bidang Keahlian
    + c. Riset Penelitian
    + +

    +
    +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    + +
    + +
    +
    +
    +@endsection diff --git a/resources/views/pages/home/pendahuluan.blade.php b/resources/views/pages/home/pendahuluan.blade.php new file mode 100644 index 0000000..a52a613 --- /dev/null +++ b/resources/views/pages/home/pendahuluan.blade.php @@ -0,0 +1,371 @@ +@extends('layouts.home') + +@section('Pendahuluan', 'title') + +@section('content') +
    + +
    +
    + +
    +

    + Latar + Belakang +

    +
    + +
    +

    + Penyusunan skripsi di Program Studi D4-Teknik Informatika Politeknik Negeri Jember adalah tahap penting yang + dipengaruhi oleh kompleksitas topik, keterbatasan sumber daya, dan kualitas bimbingan. Penentuan dosen + pembimbing masih dilakukan secara manual, seringkali kurang transparan dan menyulitkan mahasiswa. Untuk + mengatasi masalah ini, penelitian ini mengusulkan Sistem Pendukung Keputusan (SPK) dengan metode Profile + Matching. Sistem ini membantu mencocokkan kriteria mahasiswa dengan profil dosen serta menyeimbangkan kuota + bimbingan. +

    +
    + + +
    +
    + +
    +
    +

    Rumusan Masalah

    +

    Rumusan masalah dari penulisan laporan + ini + adalah sebagai berikut

    +
    + + +
    +
    + + + + + + + + + +
    +

    Implementasi Metode

    +

    Bagaimana mengimplementasikan metode + Profile + Matching untuk sistem pendukung keputusan pemilihan dosen pembimbing skripsi?

    +
    + + +
    +
    + + + + +
    +

    Rancang Bangun Sistem

    +

    Bagaimana merancang dan membangun sistem + pendukung keputusan pemilihan dosen pembimbing skripsi?

    +
    + + +
    +
    + + + + + + + + +
    +

    Evaluasi Sistem

    +

    Bagaimana mengevaluasi sistem pendukung + keputusan dalam pemilihan dosen pembimbing skripsi menggunakan Profile Matching?

    +
    + +
    +
    +
    + +
    +
    +
    +
    +

    + Tujuan Penelitian +

    +

    + Penelitian ini bertujuan untuk mengembangkan sistem pendukung keputusan dalam pemilihan + dosen + pembimbing skripsi dengan metode Profile Matching. +

    +
    + + +
    + +
    + + + + + + + + +
    +

    + Kesesuaian Profile Matching dalam Pemilihan Dosen +

    +

    + Mengetahui tingkat kesesuaian metode Profile Matching dalam pemilihan dosen + pembimbing + skripsi +

    +
    +
    + + + +
    + + + + + + + + + + + + + + + + + +
    +

    + Hasil Sistem +

    +

    + Menghasilkan sistem pedukung keputusan pemilihan dosen pembimbing skripsi. +

    +
    +
    + + + +
    + + + + + + + + + + + + + + +
    +

    + Hasil Pengujian Sistem Profile Matching +

    +

    + Mengetahui hasil dari pengujian terhadap sistem pendukung keputusan menggunakan + metode + Profile Matching dalam pemilihan dosen pembimbing skripsi. +

    +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    + +
    +
    +

    + Manfaat Penelitian +

    +

    Manfaat dari penelitian ini + adalah sebagai berikut

    + + + + +
    + + +
    +
    + +
    +
    + Features Image +
    + + + + +
    + + + + + +
    +
    + +
    + + + +
    +
    +
    +
    + +
    +
    +
    +
    +@endsection diff --git a/resources/views/pages/kriteria/create.blade.php b/resources/views/pages/kriteria/create.blade.php new file mode 100644 index 0000000..16959e6 --- /dev/null +++ b/resources/views/pages/kriteria/create.blade.php @@ -0,0 +1,182 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Kriteria') }} +
    +
      +
    1. + + + Home + + + + Data Kriteria + + +
    2. +
    3. + Tambah Data Kriteria +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Kriteria + + + + +
  • +
  • + Tambah Data +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Tambah Data Kriteria') }} +

    +
    +
    + +
    + @csrf + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/kriteria/edit.blade.php b/resources/views/pages/kriteria/edit.blade.php new file mode 100644 index 0000000..5e947f5 --- /dev/null +++ b/resources/views/pages/kriteria/edit.blade.php @@ -0,0 +1,501 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Kriteria') }} +
    +
      +
    1. + + + Home + + + + Data Kriteria + + +
    2. +
    3. + Edit Data Kriteria +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Kriteria + + + + +
  • +
  • + Edit Data {{ $kriteria->kriteria_name }} +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Edit Data Kriteria') }} +

    +
    +
    + +
    + @csrf + @method('PUT') + + + @if ($errors->any()) + + @endif + + + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + + +
    + + Batal + + +
    + + +
    + +
    + +
    +
    +

    + {{ __('Data Subkriteria') }} +

    + + {{ __('Tambah Data') }} + +
    +
    + + {{-- + + + + + + + + + + @foreach ($subkriteria as $item) + + + + + + + @endforeach + +
    + + No + + + + + Nama Subkriteria + + + + + Nilai + + + + + Tindakan + + +
    {{ $loop->iteration }}{{ $item->subkriteria_name }}{{ $item->nilai->value }} + + + + + + +
    --}} + +
    +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + @foreach ($subkriteria as $item) + + + + + + + @endforeach + +
    +
    + No + + + + +
    +
    +
    + Subkriteria + + + + +
    +
    +
    + Nilai + + + + +
    +
    + Action
    + {{ $loop->iteration }} + {{ $item->subkriteria_name }} + {{ $item->nilai->value }} +
    + + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    diff --git a/resources/views/pages/kriteria/index.blade.php b/resources/views/pages/kriteria/index.blade.php new file mode 100644 index 0000000..33926a1 --- /dev/null +++ b/resources/views/pages/kriteria/index.blade.php @@ -0,0 +1,309 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Kriteria') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Data Kriteria +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + Kriteria +
  • +@endsection + + +
    + +
    +
    +

    + {{ __('Data Kriteria') }} +

    + + {{ __('Tambah Data') }} + +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + + + @foreach ($kriteria as $item) + + + + + + + + + @endforeach + +
    +
    + No + + + + +
    +
    +
    + Kode + + + + +
    +
    +
    + Kriteria + + + + +
    +
    +
    + Aspek + + + + +
    +
    +
    + Tipe + + + + +
    +
    + Action
    + {{ $loop->iteration }} + {{ $item->kode_kriteria }} + {{ $item->kriteria_name }} + {{ $item->aspek->aspek_name }} + {{ $item->tipe }} +
    + + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    diff --git a/resources/views/pages/kriteria/show.blade.php b/resources/views/pages/kriteria/show.blade.php new file mode 100644 index 0000000..708065e --- /dev/null +++ b/resources/views/pages/kriteria/show.blade.php @@ -0,0 +1,438 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Kriteria') }} +
    +
      +
    1. + + + Home + + + + Data Kriteria + + +
    2. +
    3. + Lihat Detail Data Kriteria +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Kriteria + + + + +
  • +
  • + Lihat Detail Data {{ $kriteria->kriteria_name }} +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Lihat Detail Data Kriteria') }} +

    +
    +
    + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + +
    + +
    + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + + + + + +
    + +
    + +
    +
    +

    + {{ __('Data Subkriteria') }} +

    +
    +
    + + {{-- + + + + + + + + + + @foreach ($subkriteria as $item) + + + + + + + @endforeach + +
    + + No + + + + + Nama Subkriteria + + + + + Nilai + + + + + Tindakan + + +
    {{ $loop->iteration }}{{ $item->subkriteria_name }}{{ $item->nilai->value }} + + + + + + +
    --}} + +
    +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + @foreach ($subkriteria as $item) + + + + + + + @endforeach + +
    +
    + No + + + + +
    +
    +
    + Subkriteria + + + + +
    +
    +
    + Nilai + + + + +
    +
    + Action
    + {{ $loop->iteration }} + {{ $item->subkriteria_name }} + {{ $item->nilai->value }} +
    + + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    diff --git a/resources/views/pages/mahasiswa/create.blade.php b/resources/views/pages/mahasiswa/create.blade.php new file mode 100644 index 0000000..15e3706 --- /dev/null +++ b/resources/views/pages/mahasiswa/create.blade.php @@ -0,0 +1,158 @@ + + +
    +
    + {{ __('Mahasiswa') }} +
    +
      +
    1. + + {{ Auth::user()->name }} + + + + Mahasiswa + + +
    2. +
    3. + Tambah Data Mahasiswa +
    4. +
    +
    + +
    + +
    +
    +

    + {{ __('Tambah Data Mahasiswa') }} +

    +
    +
    + +
    + @csrf + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/mahasiswa/index.blade.php b/resources/views/pages/mahasiswa/index.blade.php new file mode 100644 index 0000000..91df935 --- /dev/null +++ b/resources/views/pages/mahasiswa/index.blade.php @@ -0,0 +1,308 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Aspek') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Data Mahasiswa +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + Mahasiswa +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Data Mahasiswa') }} +

    +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + + + @foreach ($mahasiswa as $item) + + + + + + + + @endforeach + +
    +
    + No + + + + +
    +
    +
    + Nama + + + + +
    +
    +
    + NIM + + + + +
    +
    +
    + Email + + + + +
    +
    + Action
    + {{ $loop->iteration }} + {{ $item->nim }} + {{ $item->name }} + {{ $item->email }} +
    + + + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    diff --git a/resources/views/pages/profile-matching/index.blade.php b/resources/views/pages/profile-matching/index.blade.php new file mode 100644 index 0000000..6abcc6f --- /dev/null +++ b/resources/views/pages/profile-matching/index.blade.php @@ -0,0 +1,189 @@ + + + @role('admin') +
    +
    + {{ __('Data Alternatif') }} +
    +
      +
    1. + + {{ Auth::user()->name }} + + +
    2. +
    3. + Data Alternatif +
    4. +
    +
    + @endrole + + @role('mahasiswa') +
    +
    + {{ __('Daftar Dosen') }} +
    +
      +
    1. + + {{ Auth::user()->name }} + + +
    2. +
    3. + Daftar Dosen +
    4. +
    +
    + @endrole + +
    + +
    +
    + @role('admin') +

    + {{ __('Data Alternatif') }} +

    + @endrole + @role('mahasiswa') +

    + {{ __('Daftar Dosen') }} +

    + @endrole + + @role('admin') + + {{ __('Tambah Data') }} + + @endrole +
    +
    + + + + + + + + + + + + @foreach ($alternatif as $item) + + + + + + + @endforeach + +
    + + No + + + + + NIP + + + + @role('admin') + + Nama Alternatif + + + @endrole + @role('mahasiswa') + + Nama Dosen + + + @endrole + + + Tindakan + + +
    {{ $loop->iteration }}{{ $item->nip }}{{ $item->name }} + + + + + + +
    + +
    +
    diff --git a/resources/views/pages/profile-method/create.blade.php b/resources/views/pages/profile-method/create.blade.php new file mode 100644 index 0000000..79cdfef --- /dev/null +++ b/resources/views/pages/profile-method/create.blade.php @@ -0,0 +1,191 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Profile Alternatif') }} +
    +
      +
    1. + + + Home + + + + Data Profil Alternatif + + +
    2. +
    3. + Tambah Data Profil Alternatif +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Profil Alternatif + + + + +
  • +
  • + Tambah Data +
  • +@endsection + + + + +
    + +
    +
    +

    + {{ __('Tambah Data Profile') }} +

    +
    +
    + +
    + @csrf + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + + +
    + +
    + + + @foreach ($kriteria->groupBy('id_kriteria') as $id_kriteria => $kriteriaGroup) +
    + +
    + + @if ($kriteriaGroup->first()->kriteria_name == 'Keahlian Utama') +
    + +
    + @else +
    + +
    + @endif + @endforeach + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/profile-method/edit.blade.php b/resources/views/pages/profile-method/edit.blade.php new file mode 100644 index 0000000..12af918 --- /dev/null +++ b/resources/views/pages/profile-method/edit.blade.php @@ -0,0 +1,197 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Profile Alternatif') }} +
    +
      +
    1. + + + Home + + + + Data Profile Alternatif + + +
    2. +
    3. + Edit Data Profile Alternatif +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Profile Alternatif + + + + +
  • +
  • + Edit Data +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Tambah Data Profile') }} +

    +
    +
    + +
    + @csrf + @method('PUT') + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    +
    + +
    + + @foreach ($kriteria as $kriteriaItem) +
    + +
    + + @if ($kriteriaItem->kriteria_name == 'Keahlian Utama') +
    + +
    + @else +
    + +
    + @endif + @endforeach + +
    + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/profile-method/index.blade.php b/resources/views/pages/profile-method/index.blade.php new file mode 100644 index 0000000..8646955 --- /dev/null +++ b/resources/views/pages/profile-method/index.blade.php @@ -0,0 +1,312 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Profil Alternatif') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Data Profile Alternatif +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + Profil Alternatif +
  • +@endsection + + +
    + +
    +
    +

    + {{ __('Data Profile Alternatif') }} +

    + + {{ __('Tambah Data') }} + +
    +
    + +
    +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    +
    + +
    + + + + + + + + @foreach ($kriteria as $item) + + @endforeach + + + + + + @foreach ($profile_method->sortByDesc('nilai')->groupBy('id_alternatif') as $id_alternatif => $profileData) + + + + + @foreach ($kriteria as $kriteriaItem) + @php + $matchedProfiles = $profileData->where( + 'id_kriteria', + $kriteriaItem->id_kriteria, + ); + $alternatifId = $profileData->first()->id_alternatif ?? 'alt'; + $tooltipId = + 'tooltip-keahlian-' . + $kriteriaItem->id_kriteria . + '-' . + $alternatifId; + @endphp + + + @endforeach + + + + @endforeach + +
    +
    + No + + + + +
    +
    +
    + Alternatif + + + + +
    +
    +
    + {{ $item->kode_kriteria }} + + + + +
    +
    + Action
    + {{ $loop->iteration }} + {{ $profileData->first()->alternatif->name }} + + + @if ($kriteriaItem->kriteria_name === 'Keahlian Utama') + @if ($matchedProfiles->isNotEmpty()) + {{ $matchedProfiles->pluck('subkriteria.nilai.value')->implode(' - ') }} + @else + - + @endif + @else + @php + $single = $matchedProfiles->first(); + $nilai = $single ? $single->subkriteria->nilai->value : '-'; + @endphp + {{ $nilai }} + @endif + + + +
    + + + +
    +
    +
    + + +
    +
    +
    +
    + +
    +
    diff --git a/resources/views/pages/profile-method/show.blade.php b/resources/views/pages/profile-method/show.blade.php new file mode 100644 index 0000000..e69de29 diff --git a/resources/views/pages/settings/system.blade.php b/resources/views/pages/settings/system.blade.php new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/resources/views/pages/settings/system.blade.php @@ -0,0 +1 @@ + diff --git a/resources/views/pages/subkriteria/create.blade.php b/resources/views/pages/subkriteria/create.blade.php new file mode 100644 index 0000000..1ca666e --- /dev/null +++ b/resources/views/pages/subkriteria/create.blade.php @@ -0,0 +1,166 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Subkriteria') }} +
    +
      +
    1. + + + Home + + + + Data Subkriteria + + +
    2. +
    3. + Tambah Data Subkriteria +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Subkriteria + + + + +
  • +
  • + Tambah Data +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Tambah Data Subkriteria') }} +

    +
    +
    + +
    + @csrf + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/subkriteria/edit.blade.php b/resources/views/pages/subkriteria/edit.blade.php new file mode 100644 index 0000000..e233591 --- /dev/null +++ b/resources/views/pages/subkriteria/edit.blade.php @@ -0,0 +1,183 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Subkriteria') }} +
    +
      +
    1. + + + Home + + + + Data Subkriteria + + +
    2. +
    3. + Edit Data Subkriteria +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Subkriteria + + + + +
  • +
  • + Edit Data +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Edit Data Subkriteria') }} +

    +
    +
    + +
    + @csrf + @method('PUT') + + + @if ($errors->any()) + + @endif + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + + +
    + + +
    + + Batal + + +
    +
    + +
    +
    diff --git a/resources/views/pages/subkriteria/index.blade.php b/resources/views/pages/subkriteria/index.blade.php new file mode 100644 index 0000000..d38d87e --- /dev/null +++ b/resources/views/pages/subkriteria/index.blade.php @@ -0,0 +1,143 @@ + + +
    +
    + {{ __('Data Subkriteria') }} +
    +
      +
    1. + + {{ Auth::user()->name }} + + +
    2. +
    3. + Data Subkriteria +
    4. +
    +
    + +
    + +
    +
    +

    + {{ __('Data Subkriteria') }} +

    + + {{ __('Tambah Data') }} + +
    +
    + + + + + + + + + + + + + @foreach ($subkriteria as $item) + + + + + + + + @endforeach + +
    + + No + + + + + Nama Kriteria + + + + + Nama Subkriteria + + + + + Nilai + + + + + Tindakan + + +
    {{ $loop->iteration }}{{ $item->kriteria->kriteria_name }}{{ $item->subkriteria_name }}{{ $item->nilai }} + + + + + + +
    + +
    +
    diff --git a/resources/views/pages/subkriteria/show.blade.php b/resources/views/pages/subkriteria/show.blade.php new file mode 100644 index 0000000..f46dac5 --- /dev/null +++ b/resources/views/pages/subkriteria/show.blade.php @@ -0,0 +1,147 @@ +@section('Breadcrumb') +
    +
    + {{ __('Data Subkriteria') }} +
    +
      +
    1. + + + Home + + + + Data Subkriteria + + +
    2. +
    3. + Tambah Data Subkriteria +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + + Subkriteria + + + + +
  • +
  • + Lihat Detail Data +
  • +@endsection + + + +
    + +
    +
    +

    + {{ __('Edit Data Subkriteria') }} +

    +
    +
    + +
    + +
    + +
    + +
    + +
    + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + + +
    + + + + +
    +
    diff --git a/resources/views/pages/submissions/create.blade.php b/resources/views/pages/submissions/create.blade.php new file mode 100644 index 0000000..6233888 --- /dev/null +++ b/resources/views/pages/submissions/create.blade.php @@ -0,0 +1,271 @@ +@role('mahasiswa') + @section('Breadcrumb') +
    +
    + {{ __('Pengajuan Judul') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Pengajuan Judul +
    4. +
    +
    + @endsection + + @section('mobile-breadcrumb') +
  • + Pengajuan Judul +
  • + @endsection +@endrole + + +
    + + @role('mahasiswa') + @if ($izin->izin !== 'on') +
    +
    + +
    + +
    +
    + fyi +

    Oops.

    +

    Maaf, pengajuan judul belum dibuka, silahkan + hubungi Admin.

    + +
    +
    + +
    +
    +

    ©SPK_DOSBING All Rights Reserved. 2025. +

    +
    +
    +
    + @else +
    +
    +

    + {{ __('Pengajuan Judul') }} +

    + +
    +
    + + @if ($errors->any()) + + @endif + + + +
    + @csrf +
    + + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    +
    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + + +
    + +
    +
    + @endif + @endrole + +
    +
    diff --git a/resources/views/pages/submissions/index.blade.php b/resources/views/pages/submissions/index.blade.php new file mode 100644 index 0000000..3528789 --- /dev/null +++ b/resources/views/pages/submissions/index.blade.php @@ -0,0 +1,955 @@ +@role('admin') + @section('Breadcrumb') +
    +
    + {{ __('Data Pengajuan Judul') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Data Pengajuan Judul +
    4. +
    +
    + @endsection + + @section('mobile-breadcrumb') +
  • + Pengajuan Judul +
  • + @endsection +@endrole + +@role('mahasiswa') + @section('Breadcrumb') +
    +
    + {{ __('Pemilihan Dosen') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Pemilihan Dosen +
    4. +
    +
    + @endsection + + @section('mobile-breadcrumb') +
  • + Pemilihan Dosen +
  • + @endsection +@endrole + + +
    + +
    +
    + @role('admin') +

    + {{ __('Daftar Pengajuan') }} +

    + @endrole + @role('mahasiswa') +

    + {{ __('Pemilihan Dosen') }} +

    + @endrole + + @role('admin') + + @endrole +
    +
    + + @role('mahasiswa') + +
    +
    +
    +
    +
      +
    • + +
      +
      + + + 1 + + + + + + + +
    • + +
    • +
      +
      + + + 2 + + + + + + + +
    • + + {{--
    • + + + 3 + + + + + + +
    • --}} + +
    +
    + +
    + @csrf +
    + + @if ($errors->any()) + + @endif + +
    +
    +
    +

    1. Pilih + Kriteria:

    +
    +
    +
    +
    +
    + +
    +
    +
    +

    2. Pilih + Maksimal 3 Dosen:

    +
    + @php + $groupedProfiles = $profile->groupBy('id_alternatif'); + @endphp + + @foreach ($groupedProfiles as $alternatifId => $items) + @php + $firstItem = $items->first(); + $subkriteriaIds = $items + ->pluck('subkriteria.id_subkriteria') + ->filter() + ->values(); + @endphp + +
    +
    +
    +
    + + +
    + + +
    +
    +
    + + + @endforeach +
    +
    +
    +
    + + + +
    + + + + +
    +
    +
    +
    +
    +
    + + @endrole + + @role('admin') +
    +
    +
    +
    +
    +
    + + +
    + + + + +
    +
    +
    + +
    + + + + + + + + + + + + + + {{-- --}} + + + + @foreach ($submission as $item) + + + + + + @foreach ($item->submission_detail as $detail) + + @endforeach + {{-- --}} + + @endforeach + +
    +
    + No + + + + +
    +
    +
    + NIM + + + + +
    +
    +
    + Nama Mahasiswa + + + + +
    +
    +
    + Judul + + + + +
    +
    +
    + Dosen Pembimbing + + + + +
    +
    + Action
    + {{ $loop->iteration }} + {{ $item->users->nim }} + {{ $item->users->name }} + {{ $item->judul }} + {{ $detail->alternatif->name ?? '-' }} +
    + + + +
    +
    +
    + + +
    +
    +
    +
    + @endrole + +
    + + +
    diff --git a/resources/views/partials/content.blade.php b/resources/views/partials/content.blade.php new file mode 100644 index 0000000..973cac8 --- /dev/null +++ b/resources/views/partials/content.blade.php @@ -0,0 +1,7 @@ +@include('partials.header') + +
    + @yield('content') +
    + +@include('partials.footer') diff --git a/resources/views/partials/footer.blade.php b/resources/views/partials/footer.blade.php new file mode 100644 index 0000000..bc85c65 --- /dev/null +++ b/resources/views/partials/footer.blade.php @@ -0,0 +1,43 @@ +
    +
    +
    +
    +

    SPK PDP-PM

    +

    Decision Support System using Profile Matching Method

    +
    +
    +

    Quick Links

    + +
    +
    +

    Contact

    +

    Email: info@achmadzakariya.my.id

    +

    Phone: +62 878 330 2407

    +
    +
    +

    Follow Us

    + +
    +
    +
    + © 2025 SPK PDP-PM. All rights reserved. +
    +
    +
    diff --git a/resources/views/partials/header.blade.php b/resources/views/partials/header.blade.php new file mode 100644 index 0000000..7594da6 --- /dev/null +++ b/resources/views/partials/header.blade.php @@ -0,0 +1,214 @@ + +
    + + +
    diff --git a/resources/views/partials/metadata.blade.php b/resources/views/partials/metadata.blade.php new file mode 100644 index 0000000..66ba9d9 --- /dev/null +++ b/resources/views/partials/metadata.blade.php @@ -0,0 +1,6 @@ + + + + +Laravel + diff --git a/resources/views/partials/scripts.blade.php b/resources/views/partials/scripts.blade.php new file mode 100644 index 0000000..e537889 --- /dev/null +++ b/resources/views/partials/scripts.blade.php @@ -0,0 +1,5 @@ +{{-- flowbite --}} + + + +@yield('scripts') diff --git a/resources/views/partials/styles.blade.php b/resources/views/partials/styles.blade.php new file mode 100644 index 0000000..e4ef075 --- /dev/null +++ b/resources/views/partials/styles.blade.php @@ -0,0 +1,14 @@ + + + + + + + + + +@if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot'))) + @vite(['resources/css/app.css', 'resources/js/app.js']) +@endif diff --git a/resources/views/profile-matching/result.blade.php b/resources/views/profile-matching/result.blade.php new file mode 100644 index 0000000..78769f3 --- /dev/null +++ b/resources/views/profile-matching/result.blade.php @@ -0,0 +1,443 @@ +@section('Breadcrumb') +
    +
    + {{ __('Pemilihan Dosen') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Pemilihan Dosen +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + Pemilihan Dosen +
  • +@endsection + + + +
    + +
    +
    + @role('mahasiswa') +

    + {{ __('Perhitungan Metode Profile Matching') }} +

    + @endrole +
    +
    +
    +
    + Hasil + Rekomendasi Dosen berdasarkan Metode Profile Matching: +
    +
    +
    +
    + + @role('mahasiswa') +
    +
    +
    +
    + +
    + + +
    +
    + + + + + + + @foreach ($data_kriteria as $item) + + @endforeach + + + + @foreach ($resultsGAP as $alternatif => $kriteria) + + + + @foreach ($kriteria as $item) + + @endforeach + + @endforeach + + + + + @foreach ($selectedKriteria as $item) + @php + $subkriteria = \App\Models\Subkriteria::with('nilai')->find( + $item, + ); + @endphp + + @endforeach + + +
    + No + + Kode Alternatif + + {{ $item->kode_kriteria }} +
    + {{ $loop->iteration }} + + {{ $alternatif }} + + {{ is_array($item) ? implode(', ', $item) : $item }} +
    Data + Kriteria Mahasiswa + {{ $subkriteria->nilai->value ?? 'Tidak ada nilai' }}
    + +
    +
    +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    +
    +
    +
    + @endrole +
    +
    diff --git a/resources/views/profile/edit.blade.php b/resources/views/profile/edit.blade.php new file mode 100644 index 0000000..44cfb7a --- /dev/null +++ b/resources/views/profile/edit.blade.php @@ -0,0 +1,187 @@ +@section('Breadcrumb') +
    +
    + {{ __('Profil Akun') }} +
    +
      +
    1. + + + Home + + +
    2. +
    3. + Profil Akun +
    4. +
    +
    +@endsection + +@section('mobile-breadcrumb') +
  • + Profil Akun +
  • +@endsection + + + +

    + {{ __('Profile') }} +

    +
    + +
    +
    + +
    + @csrf + @method('patch') + +
    +
    +
    + +
    + +
    +
    + + +
    + Avatar + + {{--
    + + +
    --}} +
    +
    + + @if ($errors->any()) + + @endif + + @if (session('status') === 'profile-updated') + + @endif + +
    + + + +
    + +
    + + + +
    + +
    + + + +
    + +
    + + + +
    +
    + + +
    + +
    +
    +
    + +
    + +
    +
    +
    diff --git a/resources/views/profile/partials/delete-user-form.blade.php b/resources/views/profile/partials/delete-user-form.blade.php new file mode 100644 index 0000000..b3a6382 --- /dev/null +++ b/resources/views/profile/partials/delete-user-form.blade.php @@ -0,0 +1,55 @@ +
    +
    +

    + {{ __('Delete Account') }} +

    + +

    + {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }} +

    +
    + + {{ __('Delete Account') }} + + +
    + @csrf + @method('delete') + +

    + {{ __('Are you sure you want to delete your account?') }} +

    + +

    + {{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }} +

    + +
    + + + + + +
    + +
    + + {{ __('Cancel') }} + + + + {{ __('Delete Account') }} + +
    +
    +
    +
    diff --git a/resources/views/profile/partials/update-password-form.blade.php b/resources/views/profile/partials/update-password-form.blade.php new file mode 100644 index 0000000..acd200d --- /dev/null +++ b/resources/views/profile/partials/update-password-form.blade.php @@ -0,0 +1,48 @@ +
    +
    +

    + {{ __('Update Password') }} +

    + +

    + {{ __('Ensure your account is using a long, random password to stay secure.') }} +

    +
    + +
    + @csrf + @method('put') + +
    + + + +
    + +
    + + + +
    + +
    + + + +
    + +
    + {{ __('Save') }} + + @if (session('status') === 'password-updated') +

    {{ __('Saved.') }}

    + @endif +
    +
    +
    diff --git a/resources/views/profile/partials/update-profile-information-form.blade.php b/resources/views/profile/partials/update-profile-information-form.blade.php new file mode 100644 index 0000000..7273fff --- /dev/null +++ b/resources/views/profile/partials/update-profile-information-form.blade.php @@ -0,0 +1,64 @@ +
    +
    +

    + {{ __('Profile Information') }} +

    + +

    + {{ __("Update your account's profile information and email address.") }} +

    +
    + +
    + @csrf +
    + +
    + @csrf + @method('patch') + +
    + + + +
    + +
    + + + + + @if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail()) +
    +

    + {{ __('Your email address is unverified.') }} + + +

    + + @if (session('status') === 'verification-link-sent') +

    + {{ __('A new verification link has been sent to your email address.') }} +

    + @endif +
    + @endif +
    + +
    + {{ __('Save') }} + + @if (session('status') === 'profile-updated') +

    {{ __('Saved.') }}

    + @endif +
    +
    +
    diff --git a/resources/views/vendor/mail/html/button.blade.php b/resources/views/vendor/mail/html/button.blade.php new file mode 100644 index 0000000..4a9bf7d --- /dev/null +++ b/resources/views/vendor/mail/html/button.blade.php @@ -0,0 +1,24 @@ +@props([ + 'url', + 'color' => 'primary', + 'align' => 'center', +]) + + + + + diff --git a/resources/views/vendor/mail/html/footer.blade.php b/resources/views/vendor/mail/html/footer.blade.php new file mode 100644 index 0000000..3ff41f8 --- /dev/null +++ b/resources/views/vendor/mail/html/footer.blade.php @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/resources/views/vendor/mail/html/header.blade.php b/resources/views/vendor/mail/html/header.blade.php new file mode 100644 index 0000000..56197f8 --- /dev/null +++ b/resources/views/vendor/mail/html/header.blade.php @@ -0,0 +1,12 @@ +@props(['url']) + + + +@if (trim($slot) === 'Laravel') + +@else +{{ $slot }} +@endif + + + diff --git a/resources/views/vendor/mail/html/layout.blade.php b/resources/views/vendor/mail/html/layout.blade.php new file mode 100644 index 0000000..d31a01d --- /dev/null +++ b/resources/views/vendor/mail/html/layout.blade.php @@ -0,0 +1,58 @@ + + + +{{ config('app.name') }} + + + + + +{{ $head ?? '' }} + + + + + + + + + + diff --git a/resources/views/vendor/mail/html/message.blade.php b/resources/views/vendor/mail/html/message.blade.php new file mode 100644 index 0000000..1a874fc --- /dev/null +++ b/resources/views/vendor/mail/html/message.blade.php @@ -0,0 +1,27 @@ + +{{-- Header --}} + + +{{ config('app.name') }} + + + +{{-- Body --}} +{{ $slot }} + +{{-- Subcopy --}} +@isset($subcopy) + + +{{ $subcopy }} + + +@endisset + +{{-- Footer --}} + + +© {{ date('Y') }} {{ config('app.name') }}. {{ __('All rights reserved.') }} + + + diff --git a/resources/views/vendor/mail/html/panel.blade.php b/resources/views/vendor/mail/html/panel.blade.php new file mode 100644 index 0000000..2975a60 --- /dev/null +++ b/resources/views/vendor/mail/html/panel.blade.php @@ -0,0 +1,14 @@ + + + + + + diff --git a/resources/views/vendor/mail/html/subcopy.blade.php b/resources/views/vendor/mail/html/subcopy.blade.php new file mode 100644 index 0000000..790ce6c --- /dev/null +++ b/resources/views/vendor/mail/html/subcopy.blade.php @@ -0,0 +1,7 @@ + + + + + diff --git a/resources/views/vendor/mail/html/table.blade.php b/resources/views/vendor/mail/html/table.blade.php new file mode 100644 index 0000000..a5f3348 --- /dev/null +++ b/resources/views/vendor/mail/html/table.blade.php @@ -0,0 +1,3 @@ +
    +{{ Illuminate\Mail\Markdown::parse($slot) }} +
    diff --git a/resources/views/vendor/mail/html/themes/default.css b/resources/views/vendor/mail/html/themes/default.css new file mode 100644 index 0000000..09e31d8 --- /dev/null +++ b/resources/views/vendor/mail/html/themes/default.css @@ -0,0 +1,291 @@ +/* Base */ + +body, +body *:not(html):not(style):not(br):not(tr):not(code) { + box-sizing: border-box; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, + 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + position: relative; +} + +body { + -webkit-text-size-adjust: none; + background-color: #ffffff; + color: #718096; + height: 100%; + line-height: 1.4; + margin: 0; + padding: 0; + width: 100% !important; +} + +p, +ul, +ol, +blockquote { + line-height: 1.4; + text-align: left; +} + +a { + color: #3869d4; +} + +a img { + border: none; +} + +/* Typography */ + +h1 { + color: #3d4852; + font-size: 18px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +h2 { + font-size: 16px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +h3 { + font-size: 14px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +p { + font-size: 16px; + line-height: 1.5em; + margin-top: 0; + text-align: left; +} + +p.sub { + font-size: 12px; +} + +img { + max-width: 100%; +} + +/* Layout */ + +.wrapper { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + background-color: #edf2f7; + margin: 0; + padding: 0; + width: 100%; +} + +.content { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + margin: 0; + padding: 0; + width: 100%; +} + +/* Header */ + +.header { + padding: 25px 0; + text-align: center; +} + +.header a { + color: #3d4852; + font-size: 19px; + font-weight: bold; + text-decoration: none; +} + +/* Logo */ + +.logo { + height: 75px; + max-height: 75px; + width: 75px; +} + +/* Body */ + +.body { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + background-color: #edf2f7; + border-bottom: 1px solid #edf2f7; + border-top: 1px solid #edf2f7; + margin: 0; + padding: 0; + width: 100%; +} + +.inner-body { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 570px; + background-color: #ffffff; + border-color: #e8e5ef; + border-radius: 2px; + border-width: 1px; + box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015); + margin: 0 auto; + padding: 0; + width: 570px; +} + +/* Subcopy */ + +.subcopy { + border-top: 1px solid #e8e5ef; + margin-top: 25px; + padding-top: 25px; +} + +.subcopy p { + font-size: 14px; +} + +/* Footer */ + +.footer { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 570px; + margin: 0 auto; + padding: 0; + text-align: center; + width: 570px; +} + +.footer p { + color: #b0adc5; + font-size: 12px; + text-align: center; +} + +.footer a { + color: #b0adc5; + text-decoration: underline; +} + +/* Tables */ + +.table table { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + margin: 30px auto; + width: 100%; +} + +.table th { + border-bottom: 1px solid #edeff2; + margin: 0; + padding-bottom: 8px; +} + +.table td { + color: #74787e; + font-size: 15px; + line-height: 18px; + margin: 0; + padding: 10px 0; +} + +.content-cell { + max-width: 100vw; + padding: 32px; +} + +/* Buttons */ + +.action { + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; + margin: 30px auto; + padding: 0; + text-align: center; + width: 100%; + float: unset; +} + +.button { + -webkit-text-size-adjust: none; + border-radius: 4px; + color: #fff; + display: inline-block; + overflow: hidden; + text-decoration: none; +} + +.button-blue, +.button-primary { + background-color: #2d3748; + border-bottom: 8px solid #2d3748; + border-left: 18px solid #2d3748; + border-right: 18px solid #2d3748; + border-top: 8px solid #2d3748; +} + +.button-green, +.button-success { + background-color: #48bb78; + border-bottom: 8px solid #48bb78; + border-left: 18px solid #48bb78; + border-right: 18px solid #48bb78; + border-top: 8px solid #48bb78; +} + +.button-red, +.button-error { + background-color: #e53e3e; + border-bottom: 8px solid #e53e3e; + border-left: 18px solid #e53e3e; + border-right: 18px solid #e53e3e; + border-top: 8px solid #e53e3e; +} + +/* Panels */ + +.panel { + border-left: #2d3748 solid 4px; + margin: 21px 0; +} + +.panel-content { + background-color: #edf2f7; + color: #718096; + padding: 16px; +} + +.panel-content p { + color: #718096; +} + +.panel-item { + padding: 0; +} + +.panel-item p:last-of-type { + margin-bottom: 0; + padding-bottom: 0; +} + +/* Utilities */ + +.break-all { + word-break: break-all; +} diff --git a/resources/views/vendor/mail/text/button.blade.php b/resources/views/vendor/mail/text/button.blade.php new file mode 100644 index 0000000..97444eb --- /dev/null +++ b/resources/views/vendor/mail/text/button.blade.php @@ -0,0 +1 @@ +{{ $slot }}: {{ $url }} diff --git a/resources/views/vendor/mail/text/footer.blade.php b/resources/views/vendor/mail/text/footer.blade.php new file mode 100644 index 0000000..3338f62 --- /dev/null +++ b/resources/views/vendor/mail/text/footer.blade.php @@ -0,0 +1 @@ +{{ $slot }} diff --git a/resources/views/vendor/mail/text/header.blade.php b/resources/views/vendor/mail/text/header.blade.php new file mode 100644 index 0000000..97444eb --- /dev/null +++ b/resources/views/vendor/mail/text/header.blade.php @@ -0,0 +1 @@ +{{ $slot }}: {{ $url }} diff --git a/resources/views/vendor/mail/text/layout.blade.php b/resources/views/vendor/mail/text/layout.blade.php new file mode 100644 index 0000000..ec58e83 --- /dev/null +++ b/resources/views/vendor/mail/text/layout.blade.php @@ -0,0 +1,9 @@ +{!! strip_tags($header ?? '') !!} + +{!! strip_tags($slot) !!} +@isset($subcopy) + +{!! strip_tags($subcopy) !!} +@endisset + +{!! strip_tags($footer ?? '') !!} diff --git a/resources/views/vendor/mail/text/message.blade.php b/resources/views/vendor/mail/text/message.blade.php new file mode 100644 index 0000000..80bce21 --- /dev/null +++ b/resources/views/vendor/mail/text/message.blade.php @@ -0,0 +1,27 @@ + + {{-- Header --}} + + + {{ config('app.name') }} + + + + {{-- Body --}} + {{ $slot }} + + {{-- Subcopy --}} + @isset($subcopy) + + + {{ $subcopy }} + + + @endisset + + {{-- Footer --}} + + + © {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.') + + + diff --git a/resources/views/vendor/mail/text/panel.blade.php b/resources/views/vendor/mail/text/panel.blade.php new file mode 100644 index 0000000..3338f62 --- /dev/null +++ b/resources/views/vendor/mail/text/panel.blade.php @@ -0,0 +1 @@ +{{ $slot }} diff --git a/resources/views/vendor/mail/text/subcopy.blade.php b/resources/views/vendor/mail/text/subcopy.blade.php new file mode 100644 index 0000000..3338f62 --- /dev/null +++ b/resources/views/vendor/mail/text/subcopy.blade.php @@ -0,0 +1 @@ +{{ $slot }} diff --git a/resources/views/vendor/mail/text/table.blade.php b/resources/views/vendor/mail/text/table.blade.php new file mode 100644 index 0000000..3338f62 --- /dev/null +++ b/resources/views/vendor/mail/text/table.blade.php @@ -0,0 +1 @@ +{{ $slot }} diff --git a/resources/views/vendor/notifications/email.blade.php b/resources/views/vendor/notifications/email.blade.php new file mode 100644 index 0000000..79c2408 --- /dev/null +++ b/resources/views/vendor/notifications/email.blade.php @@ -0,0 +1,58 @@ + +{{-- Greeting --}} +@if (! empty($greeting)) +# {{ $greeting }} +@else +@if ($level === 'error') +# @lang('Whoops!') +@else +# @lang('Hello!') +@endif +@endif + +{{-- Intro Lines --}} +@foreach ($introLines as $line) +{{ $line }} + +@endforeach + +{{-- Action Button --}} +@isset($actionText) + $level, + default => 'primary', + }; +?> + +{{ $actionText }} + +@endisset + +{{-- Outro Lines --}} +@foreach ($outroLines as $line) +{{ $line }} + +@endforeach + +{{-- Salutation --}} +@if (! empty($salutation)) +{{ $salutation }} +@else +@lang('Regards,')
    +{{ config('app.name') }} +@endif + +{{-- Subcopy --}} +@isset($actionText) + +@lang( + "If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\n". + 'into your web browser:', + [ + 'actionText' => $actionText, + ] +) [{{ $displayableActionUrl }}]({{ $actionUrl }}) + +@endisset +
    diff --git a/resources/views/vendor/pagination/bootstrap-4.blade.php b/resources/views/vendor/pagination/bootstrap-4.blade.php new file mode 100644 index 0000000..63c6f56 --- /dev/null +++ b/resources/views/vendor/pagination/bootstrap-4.blade.php @@ -0,0 +1,46 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/bootstrap-5.blade.php b/resources/views/vendor/pagination/bootstrap-5.blade.php new file mode 100644 index 0000000..a1795a4 --- /dev/null +++ b/resources/views/vendor/pagination/bootstrap-5.blade.php @@ -0,0 +1,88 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/default.blade.php b/resources/views/vendor/pagination/default.blade.php new file mode 100644 index 0000000..0db70b5 --- /dev/null +++ b/resources/views/vendor/pagination/default.blade.php @@ -0,0 +1,46 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/semantic-ui.blade.php b/resources/views/vendor/pagination/semantic-ui.blade.php new file mode 100644 index 0000000..ef0dbb1 --- /dev/null +++ b/resources/views/vendor/pagination/semantic-ui.blade.php @@ -0,0 +1,36 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/simple-bootstrap-4.blade.php b/resources/views/vendor/pagination/simple-bootstrap-4.blade.php new file mode 100644 index 0000000..4bb4917 --- /dev/null +++ b/resources/views/vendor/pagination/simple-bootstrap-4.blade.php @@ -0,0 +1,27 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/simple-bootstrap-5.blade.php b/resources/views/vendor/pagination/simple-bootstrap-5.blade.php new file mode 100644 index 0000000..a89005e --- /dev/null +++ b/resources/views/vendor/pagination/simple-bootstrap-5.blade.php @@ -0,0 +1,29 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/simple-default.blade.php b/resources/views/vendor/pagination/simple-default.blade.php new file mode 100644 index 0000000..36bdbc1 --- /dev/null +++ b/resources/views/vendor/pagination/simple-default.blade.php @@ -0,0 +1,19 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/simple-tailwind.blade.php b/resources/views/vendor/pagination/simple-tailwind.blade.php new file mode 100644 index 0000000..ea02400 --- /dev/null +++ b/resources/views/vendor/pagination/simple-tailwind.blade.php @@ -0,0 +1,25 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/resources/views/vendor/pagination/tailwind.blade.php b/resources/views/vendor/pagination/tailwind.blade.php new file mode 100644 index 0000000..aee2ad2 --- /dev/null +++ b/resources/views/vendor/pagination/tailwind.blade.php @@ -0,0 +1,106 @@ +@if ($paginator->hasPages()) + +@endif diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..cebce7c --- /dev/null +++ b/routes/api.php @@ -0,0 +1,30 @@ +group(function () { + Route::prefix('aspek')->group(function () { + Route::get('/', [AspekController::class, 'index']); + }); + + Route::prefix('kriteria')->group(function () { + Route::get('/', [KriteriaController::class, 'index']); + }); + + Route::prefix('alternatif')->group(function () { + Route::get('/', [AlternatifController::class, 'index']); + }); + + Route::prefix('profile-method')->group(function () { + Route::get('/', [ProfileMethodController::class, 'index']); + }); + + Route::get('/user', function (Request $request) { + return $request->user(); + })->middleware('auth:sanctum'); +}); \ No newline at end of file diff --git a/routes/auth.php b/routes/auth.php new file mode 100644 index 0000000..bf1ef68 --- /dev/null +++ b/routes/auth.php @@ -0,0 +1,65 @@ +group(function () { + Route::get('register', [RegisteredUserController::class, 'create']) + ->name('register'); + + Route::post('register', [RegisteredUserController::class, 'store']); + + Route::get('login', [AuthenticatedSessionController::class, 'create']) + ->name('login'); + + Route::post('login', [AuthenticatedSessionController::class, 'store']); + + Route::get('forgot-password', [PasswordResetLinkController::class, 'create']) + ->name('password.request'); + + Route::post('forgot-password', [PasswordResetLinkController::class, 'store']) + ->name('password.email'); + + Route::get('reset-password/{token}', [NewPasswordController::class, 'create']) + ->name('password.reset'); + + Route::post('reset-password', [NewPasswordController::class, 'store']) + ->name('password.store'); + + Route::get('login/google', [GoogleController::class, 'redirectToGoogle']) + ->name('auth.google'); + + Route::get('login/google/callback', [GoogleController::class, 'handleGoogleCallback']); +}); + +Route::middleware('auth')->group(function () { + Route::get('verify-email', EmailVerificationPromptController::class) + ->name('verification.notice'); + + Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) + ->middleware(['signed', 'throttle:6,1']) + ->name('verification.verify'); + + Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) + ->middleware('throttle:6,1') + ->name('verification.send'); + + Route::get('confirm-password', [ConfirmablePasswordController::class, 'show']) + ->name('password.confirm'); + + Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']); + + Route::put('password', [PasswordController::class, 'update'])->name('password.update'); + + Route::post('logout', [AuthenticatedSessionController::class, 'destroy']) + ->name('logout'); +}); \ No newline at end of file diff --git a/routes/channels.php b/routes/channels.php new file mode 100644 index 0000000..df2ad28 --- /dev/null +++ b/routes/channels.php @@ -0,0 +1,7 @@ +id === (int) $id; +}); diff --git a/routes/console.php b/routes/console.php new file mode 100644 index 0000000..eff2ed2 --- /dev/null +++ b/routes/console.php @@ -0,0 +1,8 @@ +comment(Inspiring::quote()); +})->purpose('Display an inspiring quote')->hourly(); diff --git a/routes/web.php b/routes/web.php new file mode 100644 index 0000000..53810ed --- /dev/null +++ b/routes/web.php @@ -0,0 +1,64 @@ +group(function () { + Route::get('dashboard', [DashboardController::class, 'index'] )->name('dashboard.index'); + + Route::resource('aspek', AspekController::class); + + Route::prefix('kriteria')->name('kriteria.')->group(function () { + Route::resource('/', KriteriaController::class)->parameters(['' => 'kriteria']); + + Route::resource('/{kriteria}/subkriteria', SubkriteriaController::class) + ->parameters(['subkriteria' => 'subkriteria']); + }); + + Route::resource('alternatif', AlternatifController::class); + Route::resource('method-profile', ProfileMethodController::class); + Route::resource('mahasiswa', MahasiswaController::class); + Route::resource('submissions', SubmissionsController::class); + + Route::post('results', [ProfileMatchingController::class, 'handleProfileMatching'])->name('results.start'); + Route::post('results/{id}', [ProfileMatchingController::class, 'submit'])->name('results.submit'); + Route::get('results/{id}', [ProfileMatchingController::class, 'data'])->name('results.data'); + + Route::prefix('settings')->group(function () { + Route::post('submited-permission/update', [SystemController::class, 'updatePermission'])->name('system.permission.update'); + + Route::get('system', [SystemController::class, 'index'])->name('system.index'); + + Route::get('profile', [ProfileController::class, 'edit'])->name('profile.edit'); + Route::patch('profile', [ProfileController::class, 'update'])->name('profile.update'); + Route::delete('profile', [ProfileController::class, 'destroy'])->name('profile.destroy'); + }); +}); + +require __DIR__.'/auth.php'; \ No newline at end of file diff --git a/sail b/sail new file mode 100644 index 0000000..d9685f0 --- /dev/null +++ b/sail @@ -0,0 +1,603 @@ +#!/usr/bin/env bash + +UNAMEOUT="$(uname -s)" + +# Verify operating system is supported... +case "${UNAMEOUT}" in + Linux*) MACHINE=linux;; + Darwin*) MACHINE=mac;; + *) MACHINE="UNKNOWN" +esac + +if [ "$MACHINE" == "UNKNOWN" ]; then + echo "Unsupported operating system [$(uname -s)]. Laravel Sail supports macOS, Linux, and Windows (WSL2)." >&2 + + exit 1 +fi + +# Determine if stdout is a terminal... +if test -t 1; then + # Determine if colors are supported... + ncolors=$(tput colors) + + if test -n "$ncolors" && test "$ncolors" -ge 8; then + BOLD="$(tput bold)" + YELLOW="$(tput setaf 3)" + GREEN="$(tput setaf 2)" + NC="$(tput sgr0)" + fi +fi + +# Function that prints the available commands... +function display_help { + echo "Laravel Sail" + echo + echo "${YELLOW}Usage:${NC}" >&2 + echo " sail COMMAND [options] [arguments]" + echo + echo "Unknown commands are passed to the docker-compose binary." + echo + echo "${YELLOW}docker-compose Commands:${NC}" + echo " ${GREEN}sail up${NC} Start the application" + echo " ${GREEN}sail up -d${NC} Start the application in the background" + echo " ${GREEN}sail stop${NC} Stop the application" + echo " ${GREEN}sail restart${NC} Restart the application" + echo " ${GREEN}sail ps${NC} Display the status of all containers" + echo + echo "${YELLOW}Artisan Commands:${NC}" + echo " ${GREEN}sail artisan ...${NC} Run an Artisan command" + echo " ${GREEN}sail artisan queue:work${NC}" + echo + echo "${YELLOW}PHP Commands:${NC}" + echo " ${GREEN}sail php ...${NC} Run a snippet of PHP code" + echo " ${GREEN}sail php -v${NC}" + echo + echo "${YELLOW}Composer Commands:${NC}" + echo " ${GREEN}sail composer ...${NC} Run a Composer command" + echo " ${GREEN}sail composer require laravel/sanctum${NC}" + echo + echo "${YELLOW}Node Commands:${NC}" + echo " ${GREEN}sail node ...${NC} Run a Node command" + echo " ${GREEN}sail node --version${NC}" + echo + echo "${YELLOW}NPM Commands:${NC}" + echo " ${GREEN}sail npm ...${NC} Run a npm command" + echo " ${GREEN}sail npx${NC} Run a npx command" + echo " ${GREEN}sail npm run prod${NC}" + echo + echo "${YELLOW}PNPM Commands:${NC}" + echo " ${GREEN}sail pnpm ...${NC} Run a pnpm command" + echo " ${GREEN}sail pnpx${NC} Run a pnpx command" + echo " ${GREEN}sail pnpm run prod${NC}" + echo + echo "${YELLOW}Yarn Commands:${NC}" + echo " ${GREEN}sail yarn ...${NC} Run a Yarn command" + echo " ${GREEN}sail yarn run prod${NC}" + echo + echo "${YELLOW}Bun Commands:${NC}" + echo " ${GREEN}sail bun ...${NC} Run a bun command" + echo " ${GREEN}sail bunx${NC} Run a bunx command" + echo " ${GREEN}sail bun run prod${NC}" + echo + echo "${YELLOW}Database Commands:${NC}" + echo " ${GREEN}sail mysql${NC} Start a MySQL CLI session within the 'mysql' container" + echo " ${GREEN}sail mariadb${NC} Start a MySQL CLI session within the 'mariadb' container" + echo " ${GREEN}sail psql${NC} Start a PostgreSQL CLI session within the 'pgsql' container" + echo " ${GREEN}sail mongodb${NC} Start a Mongo Shell session within the 'mongodb' container" + echo " ${GREEN}sail redis${NC} Start a Redis CLI session within the 'redis' container" + echo + echo "${YELLOW}Debugging:${NC}" + echo " ${GREEN}sail debug ...${NC} Run an Artisan command in debug mode" + echo " ${GREEN}sail debug queue:work${NC}" + echo + echo "${YELLOW}Running Tests:${NC}" + echo " ${GREEN}sail test${NC} Run the PHPUnit tests via the Artisan test command" + echo " ${GREEN}sail phpunit ...${NC} Run PHPUnit" + echo " ${GREEN}sail pest ...${NC} Run Pest" + echo " ${GREEN}sail pint ...${NC} Run Pint" + echo " ${GREEN}sail dusk${NC} Run the Dusk tests (Requires the laravel/dusk package)" + echo " ${GREEN}sail dusk:fails${NC} Re-run previously failed Dusk tests (Requires the laravel/dusk package)" + echo + echo "${YELLOW}Container CLI:${NC}" + echo " ${GREEN}sail shell${NC} Start a shell session within the application container" + echo " ${GREEN}sail bash${NC} Alias for 'sail shell'" + echo " ${GREEN}sail root-shell${NC} Start a root shell session within the application container" + echo " ${GREEN}sail root-bash${NC} Alias for 'sail root-shell'" + echo " ${GREEN}sail tinker${NC} Start a new Laravel Tinker session" + echo + echo "${YELLOW}Sharing:${NC}" + echo " ${GREEN}sail share${NC} Share the application publicly via a temporary URL" + echo " ${GREEN}sail open${NC} Open the site in your browser" + echo + echo "${YELLOW}Binaries:${NC}" + echo " ${GREEN}sail bin ...${NC} Run Composer binary scripts from the vendor/bin directory" + echo + echo "${YELLOW}Customization:${NC}" + echo " ${GREEN}sail artisan sail:publish${NC} Publish the Sail configuration files" + echo " ${GREEN}sail build --no-cache${NC} Rebuild all of the Sail containers" + + exit 1 +} + +# Proxy the "help" command... +if [ $# -gt 0 ]; then + if [ "$1" == "help" ] || [ "$1" == "-h" ] || [ "$1" == "-help" ] || [ "$1" == "--help" ]; then + display_help + fi +else + display_help +fi + +# Source the ".env" file so Laravel's environment variables are available... +# shellcheck source=/dev/null +if [ -n "$APP_ENV" ] && [ -f ./.env."$APP_ENV" ]; then + source ./.env."$APP_ENV"; +elif [ -f ./.env ]; then + source ./.env; +fi + +# Define environment variables... +export APP_PORT=${APP_PORT:-80} +export APP_SERVICE=${APP_SERVICE:-"laravel.test"} +export DB_PORT=${DB_PORT:-3306} +export WWWUSER=${WWWUSER:-$UID} +export WWWGROUP=${WWWGROUP:-$(id -g)} + +export SAIL_FILES=${SAIL_FILES:-""} +export SAIL_SHARE_DASHBOARD=${SAIL_SHARE_DASHBOARD:-4040} +export SAIL_SHARE_SERVER_HOST=${SAIL_SHARE_SERVER_HOST:-"laravel-sail.site"} +export SAIL_SHARE_SERVER_PORT=${SAIL_SHARE_SERVER_PORT:-8080} +export SAIL_SHARE_SUBDOMAIN=${SAIL_SHARE_SUBDOMAIN:-""} +export SAIL_SHARE_DOMAIN=${SAIL_SHARE_DOMAIN:-"$SAIL_SHARE_SERVER_HOST"} +export SAIL_SHARE_SERVER=${SAIL_SHARE_SERVER:-""} + +# Function that outputs Sail is not running... +function sail_is_not_running { + echo "${BOLD}Sail is not running.${NC}" >&2 + echo "" >&2 + echo "${BOLD}You may Sail using the following commands:${NC} './vendor/bin/sail up' or './vendor/bin/sail up -d'" >&2 + + exit 1 +} + +# Define Docker Compose command prefix... +if docker compose &> /dev/null; then + DOCKER_COMPOSE=(docker compose) +else + DOCKER_COMPOSE=(docker-compose) +fi + +if [ -n "$SAIL_FILES" ]; then + # Convert SAIL_FILES to an array... + IFS=':' read -ra SAIL_FILES <<< "$SAIL_FILES" + + for FILE in "${SAIL_FILES[@]}"; do + if [ -f "$FILE" ]; then + DOCKER_COMPOSE+=(-f "$FILE") + else + echo "${BOLD}Unable to find Docker Compose file: '${FILE}'${NC}" >&2 + + exit 1 + fi + done +fi + +EXEC="yes" + +if [ -z "$SAIL_SKIP_CHECKS" ]; then + # Ensure that Docker is running... + if ! docker info > /dev/null 2>&1; then + echo "${BOLD}Docker is not running.${NC}" >&2 + + exit 1 + fi + + # Determine if Sail is currently up... + if "${DOCKER_COMPOSE[@]}" ps "$APP_SERVICE" 2>&1 | grep 'Exit\|exited'; then + echo "${BOLD}Shutting down old Sail processes...${NC}" >&2 + + "${DOCKER_COMPOSE[@]}" down > /dev/null 2>&1 + + EXEC="no" + elif [ -z "$("${DOCKER_COMPOSE[@]}" ps -q)" ]; then + EXEC="no" + fi +fi + +ARGS=() + +# Proxy PHP commands to the "php" binary on the application container... +if [ "$1" == "php" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" "php") + else + sail_is_not_running + fi + +# Proxy vendor binary commands on the application container... +elif [ "$1" == "bin" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + CMD=$1 + shift 1 + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" ./vendor/bin/"$CMD") + else + sail_is_not_running + fi + +# Proxy docker-compose commands to the docker-compose binary on the application container... +elif [ "$1" == "docker-compose" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" "${DOCKER_COMPOSE[@]}") + else + sail_is_not_running + fi + +# Proxy Composer commands to the "composer" binary on the application container... +elif [ "$1" == "composer" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" "composer") + else + sail_is_not_running + fi + +# Proxy Artisan commands to the "artisan" binary on the application container... +elif [ "$1" == "artisan" ] || [ "$1" == "art" ] || [ "$1" == "a" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" php artisan) + else + sail_is_not_running + fi + +# Proxy the "debug" command to the "php artisan" binary on the application container with xdebug enabled... +elif [ "$1" == "debug" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail -e XDEBUG_TRIGGER=1) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" php artisan) + else + sail_is_not_running + fi + +# Proxy the "test" command to the "php artisan test" Artisan command... +elif [ "$1" == "test" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" php artisan test) + else + sail_is_not_running + fi + +# Proxy the "phpunit" command to "php vendor/bin/phpunit"... +elif [ "$1" == "phpunit" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" php vendor/bin/phpunit) + else + sail_is_not_running + fi + +# Proxy the "pest" command to "php vendor/bin/pest"... +elif [ "$1" == "pest" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" php vendor/bin/pest) + else + sail_is_not_running + fi + +# Proxy the "pint" command to "php vendor/bin/pint"... +elif [ "$1" == "pint" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" php vendor/bin/pint) + else + sail_is_not_running + fi + +# Proxy the "dusk" command to the "php artisan dusk" Artisan command... +elif [ "$1" == "dusk" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=(-e "APP_URL=http://${APP_SERVICE}") + ARGS+=(-e "DUSK_DRIVER_URL=http://selenium:4444/wd/hub") + ARGS+=("$APP_SERVICE" php artisan dusk) + else + sail_is_not_running + fi + +# Proxy the "dusk:fails" command to the "php artisan dusk:fails" Artisan command... +elif [ "$1" == "dusk:fails" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=(-e "APP_URL=http://${APP_SERVICE}") + ARGS+=(-e "DUSK_DRIVER_URL=http://selenium:4444/wd/hub") + ARGS+=("$APP_SERVICE" php artisan dusk:fails) + else + sail_is_not_running + fi + +# Initiate a Laravel Tinker session within the application container... +elif [ "$1" == "tinker" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" php artisan tinker) + else + sail_is_not_running + fi + +# Proxy Node commands to the "node" binary on the application container... +elif [ "$1" == "node" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" node) + else + sail_is_not_running + fi + +# Proxy NPM commands to the "npm" binary on the application container... +elif [ "$1" == "npm" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" npm) + else + sail_is_not_running + fi + +# Proxy NPX commands to the "npx" binary on the application container... +elif [ "$1" == "npx" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" npx) + else + sail_is_not_running + fi + +# Proxy PNPM commands to the "pnpm" binary on the application container... +elif [ "$1" == "pnpm" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" pnpm) + else + sail_is_not_running + fi + +# Proxy PNPX commands to the "pnpx" binary on the application container... +elif [ "$1" == "pnpx" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" pnpx) + else + sail_is_not_running + fi + +# Proxy Yarn commands to the "yarn" binary on the application container... +elif [ "$1" == "yarn" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" yarn) + else + sail_is_not_running + fi + +# Proxy Bun commands to the "bun" binary on the application container... +elif [ "$1" == "bun" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" bun) + else + sail_is_not_running + fi + +# Proxy Bun X commands to the "bunx" binary on the application container... +elif [ "$1" == "bunx" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" bunx) + else + sail_is_not_running + fi + +# Initiate a MySQL CLI terminal session within the "mysql" container... +elif [ "$1" == "mysql" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=(mysql bash -c) + ARGS+=("MYSQL_PWD=\${MYSQL_PASSWORD} mysql -u \${MYSQL_USER} \${MYSQL_DATABASE}") + else + sail_is_not_running + fi + +# Initiate a MySQL CLI terminal session within the "mariadb" container... +elif [ "$1" == "mariadb" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=(mariadb bash -c) + ARGS+=("MYSQL_PWD=\${MYSQL_PASSWORD} mariadb -u \${MYSQL_USER} \${MYSQL_DATABASE}") + else + sail_is_not_running + fi + +# Initiate a PostgreSQL CLI terminal session within the "pgsql" container... +elif [ "$1" == "psql" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=(pgsql bash -c) + ARGS+=("PGPASSWORD=\${PGPASSWORD} psql -U \${POSTGRES_USER} \${POSTGRES_DB}") + else + sail_is_not_running + fi + +# Initiate a Bash shell within the application container... +elif [ "$1" == "shell" ] || [ "$1" == "bash" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u sail) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" bash) + else + sail_is_not_running + fi + +# Initiate a root user Bash shell within the application container... +elif [ "$1" == "root-shell" ] || [ "$1" == "root-bash" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec -u root) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=("$APP_SERVICE" bash) + else + sail_is_not_running + fi + +# Initiate a MongoDB Shell within the "mongodb" container... +elif [ "$1" == "mongodb" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=(mongodb mongosh --port "${FORWARD_MONGODB_PORT:-27017}" --username "$MONGODB_USERNAME" --password "$MONGODB_PASSWORD" --authenticationDatabase admin) + else + sail_is_not_running + fi + +# Initiate a Redis CLI terminal session within the "redis" container... +elif [ "$1" == "redis" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=(redis redis-cli) + else + sail_is_not_running + fi + +# Share the site... +elif [ "$1" == "share" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + docker run --init --rm --add-host=host.docker.internal:host-gateway -p "$SAIL_SHARE_DASHBOARD":4040 -t beyondcodegmbh/expose-server:latest share http://host.docker.internal:"$APP_PORT" \ + --server-host="$SAIL_SHARE_SERVER_HOST" \ + --server-port="$SAIL_SHARE_SERVER_PORT" \ + --auth="$SAIL_SHARE_TOKEN" \ + --server="$SAIL_SHARE_SERVER" \ + --subdomain="$SAIL_SHARE_SUBDOMAIN" \ + --domain="$SAIL_SHARE_DOMAIN" \ + "$@" + + exit + else + sail_is_not_running + fi + +# Open the site... +elif [ "$1" == "open" ]; then + shift 1 + + if command -v open &>/dev/null; then + OPEN="open" + elif command -v xdg-open &>/dev/null; then + OPEN="xdg-open" + else + echo "Neither open nor xdg-open is available. Exiting." + exit 1 + fi + + if [ "$EXEC" == "yes" ]; then + + if [[ -n "$APP_PORT" && "$APP_PORT" != "80" ]]; then + FULL_URL="${APP_URL}:${APP_PORT}" + else + FULL_URL="$APP_URL" + fi + + $OPEN "$FULL_URL" + + exit + else + sail_is_not_running + fi +fi + +# Run Docker Compose with the defined arguments... +"${DOCKER_COMPOSE[@]}" "${ARGS[@]}" "$@" diff --git a/storage/app/.gitignore b/storage/app/.gitignore new file mode 100644 index 0000000..fedb287 --- /dev/null +++ b/storage/app/.gitignore @@ -0,0 +1,4 @@ +* +!private/ +!public/ +!.gitignore diff --git a/storage/app/private/.gitignore b/storage/app/private/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/app/private/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/app/public/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/debugbar/.gitignore b/storage/debugbar/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/debugbar/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore new file mode 100644 index 0000000..05c4471 --- /dev/null +++ b/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/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore new file mode 100644 index 0000000..01e4a6c --- /dev/null +++ b/storage/framework/cache/.gitignore @@ -0,0 +1,3 @@ +* +!data/ +!.gitignore diff --git a/storage/framework/cache/data/.gitignore b/storage/framework/cache/data/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/cache/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/sessions/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/testing/.gitignore b/storage/framework/testing/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/testing/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/framework/views/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/storage/logs/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..1508cb1 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,59 @@ +import defaultTheme from "tailwindcss/defaultTheme"; +import forms from "@tailwindcss/forms"; + +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php", + "./storage/framework/views/*.php", + "./resources/views/**/*.blade.php", + "./node_modules/flowbite/**/*.js", + "./node_modules/preline/dist/*.js", + "./node_modules/jquery/dist/jquery.min.js", + "./node_modules/datatables.net/js/dataTables.min.js", + ], + + theme: { + extend: { + keyframes: { + customBounce: { + "0%, 100%": { transform: "translateY(0)" }, + "50%": { transform: "translateY(-10px)" }, + }, + }, + fontFamily: { + primary: ["Poppins", ...defaultTheme.fontFamily.sans], + }, + colors: { + primary: "#1570EF", + secondary: "#53B1FD", + navy: "#1B2559", + purple: "#707EAE", + body: "#F4F7FE", + success: "#05CD99", + successHover: "#6FEACA", + }, + animation: { + CustomBounce : "customBounce 2s ease-in-out infinite" + } + }, + container: { + center: true, + padding: { + DEFAULT: "1rem", + sm: "2rem", + lg: "4rem", + xl: "5rem", + "2xl": "6rem", + }, + }, + }, + + plugins: [ + forms, + require("preline/plugin"), + require("flowbite/plugin")({ + datatables: true, + }), + ], +}; diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php new file mode 100644 index 0000000..13dcb7c --- /dev/null +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -0,0 +1,54 @@ +get('/login'); + + $response->assertStatus(200); + } + + public function test_users_can_authenticate_using_the_login_screen(): void + { + $user = User::factory()->create(); + + $response = $this->post('/login', [ + 'email' => $user->email, + 'password' => 'password', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(route('dashboard', absolute: false)); + } + + public function test_users_can_not_authenticate_with_invalid_password(): void + { + $user = User::factory()->create(); + + $this->post('/login', [ + 'email' => $user->email, + 'password' => 'wrong-password', + ]); + + $this->assertGuest(); + } + + public function test_users_can_logout(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/logout'); + + $this->assertGuest(); + $response->assertRedirect('/'); + } +} diff --git a/tests/Feature/Auth/EmailVerificationTest.php b/tests/Feature/Auth/EmailVerificationTest.php new file mode 100644 index 0000000..705570b --- /dev/null +++ b/tests/Feature/Auth/EmailVerificationTest.php @@ -0,0 +1,58 @@ +unverified()->create(); + + $response = $this->actingAs($user)->get('/verify-email'); + + $response->assertStatus(200); + } + + public function test_email_can_be_verified(): void + { + $user = User::factory()->unverified()->create(); + + Event::fake(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1($user->email)] + ); + + $response = $this->actingAs($user)->get($verificationUrl); + + Event::assertDispatched(Verified::class); + $this->assertTrue($user->fresh()->hasVerifiedEmail()); + $response->assertRedirect(route('dashboard', absolute: false).'?verified=1'); + } + + public function test_email_is_not_verified_with_invalid_hash(): void + { + $user = User::factory()->unverified()->create(); + + $verificationUrl = URL::temporarySignedRoute( + 'verification.verify', + now()->addMinutes(60), + ['id' => $user->id, 'hash' => sha1('wrong-email')] + ); + + $this->actingAs($user)->get($verificationUrl); + + $this->assertFalse($user->fresh()->hasVerifiedEmail()); + } +} diff --git a/tests/Feature/Auth/PasswordConfirmationTest.php b/tests/Feature/Auth/PasswordConfirmationTest.php new file mode 100644 index 0000000..ff85721 --- /dev/null +++ b/tests/Feature/Auth/PasswordConfirmationTest.php @@ -0,0 +1,44 @@ +create(); + + $response = $this->actingAs($user)->get('/confirm-password'); + + $response->assertStatus(200); + } + + public function test_password_can_be_confirmed(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/confirm-password', [ + 'password' => 'password', + ]); + + $response->assertRedirect(); + $response->assertSessionHasNoErrors(); + } + + public function test_password_is_not_confirmed_with_invalid_password(): void + { + $user = User::factory()->create(); + + $response = $this->actingAs($user)->post('/confirm-password', [ + 'password' => 'wrong-password', + ]); + + $response->assertSessionHasErrors(); + } +} diff --git a/tests/Feature/Auth/PasswordResetTest.php b/tests/Feature/Auth/PasswordResetTest.php new file mode 100644 index 0000000..aa50350 --- /dev/null +++ b/tests/Feature/Auth/PasswordResetTest.php @@ -0,0 +1,73 @@ +get('/forgot-password'); + + $response->assertStatus(200); + } + + public function test_reset_password_link_can_be_requested(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class); + } + + public function test_reset_password_screen_can_be_rendered(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) { + $response = $this->get('/reset-password/'.$notification->token); + + $response->assertStatus(200); + + return true; + }); + } + + public function test_password_can_be_reset_with_valid_token(): void + { + Notification::fake(); + + $user = User::factory()->create(); + + $this->post('/forgot-password', ['email' => $user->email]); + + Notification::assertSentTo($user, ResetPassword::class, function ($notification) use ($user) { + $response = $this->post('/reset-password', [ + 'token' => $notification->token, + 'email' => $user->email, + 'password' => 'password', + 'password_confirmation' => 'password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect(route('login')); + + return true; + }); + } +} diff --git a/tests/Feature/Auth/PasswordUpdateTest.php b/tests/Feature/Auth/PasswordUpdateTest.php new file mode 100644 index 0000000..ca28c6c --- /dev/null +++ b/tests/Feature/Auth/PasswordUpdateTest.php @@ -0,0 +1,51 @@ +create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->put('/password', [ + 'current_password' => 'password', + 'password' => 'new-password', + 'password_confirmation' => 'new-password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $this->assertTrue(Hash::check('new-password', $user->refresh()->password)); + } + + public function test_correct_password_must_be_provided_to_update_password(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->put('/password', [ + 'current_password' => 'wrong-password', + 'password' => 'new-password', + 'password_confirmation' => 'new-password', + ]); + + $response + ->assertSessionHasErrorsIn('updatePassword', 'current_password') + ->assertRedirect('/profile'); + } +} diff --git a/tests/Feature/Auth/RegistrationTest.php b/tests/Feature/Auth/RegistrationTest.php new file mode 100644 index 0000000..1489d0e --- /dev/null +++ b/tests/Feature/Auth/RegistrationTest.php @@ -0,0 +1,31 @@ +get('/register'); + + $response->assertStatus(200); + } + + public function test_new_users_can_register(): void + { + $response = $this->post('/register', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + 'password' => 'password', + 'password_confirmation' => 'password', + ]); + + $this->assertAuthenticated(); + $response->assertRedirect(route('dashboard', absolute: false)); + } +} diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php new file mode 100644 index 0000000..8364a84 --- /dev/null +++ b/tests/Feature/ExampleTest.php @@ -0,0 +1,19 @@ +get('/'); + + $response->assertStatus(200); + } +} diff --git a/tests/Feature/ProfileTest.php b/tests/Feature/ProfileTest.php new file mode 100644 index 0000000..252fdcc --- /dev/null +++ b/tests/Feature/ProfileTest.php @@ -0,0 +1,99 @@ +create(); + + $response = $this + ->actingAs($user) + ->get('/profile'); + + $response->assertOk(); + } + + public function test_profile_information_can_be_updated(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->patch('/profile', [ + 'name' => 'Test User', + 'email' => 'test@example.com', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $user->refresh(); + + $this->assertSame('Test User', $user->name); + $this->assertSame('test@example.com', $user->email); + $this->assertNull($user->email_verified_at); + } + + public function test_email_verification_status_is_unchanged_when_the_email_address_is_unchanged(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->patch('/profile', [ + 'name' => 'Test User', + 'email' => $user->email, + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/profile'); + + $this->assertNotNull($user->refresh()->email_verified_at); + } + + public function test_user_can_delete_their_account(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->delete('/profile', [ + 'password' => 'password', + ]); + + $response + ->assertSessionHasNoErrors() + ->assertRedirect('/'); + + $this->assertGuest(); + $this->assertNull($user->fresh()); + } + + public function test_correct_password_must_be_provided_to_delete_account(): void + { + $user = User::factory()->create(); + + $response = $this + ->actingAs($user) + ->from('/profile') + ->delete('/profile', [ + 'password' => 'wrong-password', + ]); + + $response + ->assertSessionHasErrorsIn('userDeletion', 'password') + ->assertRedirect('/profile'); + + $this->assertNotNull($user->fresh()); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..fe1ffc2 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,10 @@ +isEmpty()) { + $this->fail("Tidak ada data alternatif dalam database."); + } + + $submissions = []; + + foreach ($mahasiswa as [$name, $nim, $tahun]) { + $user = User::where('email', strtolower($nim) . '@student.polije.ac.id')->first(); + + if (!$user) { + Log::warning("Mahasiswa dengan NIM {$nim} tidak ditemukan."); + continue; + } + + $submission = Submissions::factory()->create(['id' => $user->id]); + $submissions[] = $submission; + } + + foreach ($submissions as $submission) { + $chosenAlternatifs = $alternatifs->shuffle()->take(1)->pluck('id_alternatif'); + + foreach ($chosenAlternatifs as $id_alternatif) { + SubmissionDetail::factory()->create([ + 'id_submission' => $submission->id_submission, + 'id_alternatif' => $id_alternatif, + ]); + } + } + + $processedSubmissions = []; + + foreach ($submissions as $submission) { + if (in_array($submission->id_submission, $processedSubmissions)) { + Log::warning("Skipping duplicate dispatch for Submission ID: {$submission->id_submission}"); + continue; + } + + $chosenAlternatifs = SubmissionDetail::where('id_submission', $submission->id_submission) + ->pluck('id_alternatif') + ->toArray(); + + if (count($chosenAlternatifs) === 1) { + // Log::info("Dispatching event for Submission ID: {$submission->id_submission}"); + // Log::info("Alternatif terpilih untuk angkatan {$submission->angkatan}: " . json_encode($chosenAlternatifs)); + // event(new SubmissionCreated($submission)); + + $processedSubmissions[] = $submission->id_submission; + } else { + Log::warning("Skipping event for Submission ID: {$submission->id_submission}, expected 1 alternatives but got " . count($chosenAlternatifs)); + } + } + + $this->assertTrue(true); + } +} \ No newline at end of file diff --git a/tests/Unit/ExampleTest.php b/tests/Unit/ExampleTest.php new file mode 100644 index 0000000..5773b0c --- /dev/null +++ b/tests/Unit/ExampleTest.php @@ -0,0 +1,16 @@ +assertTrue(true); + } +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..421b569 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import laravel from 'laravel-vite-plugin'; + +export default defineConfig({ + plugins: [ + laravel({ + input: ['resources/css/app.css', 'resources/js/app.js'], + refresh: true, + }), + ], +});