diff --git a/', 'budget_control.cabang_id', '=', 'cabangs.id') b/', 'budget_control.cabang_id', '=', 'cabangs.id') new file mode 100644 index 0000000..ff7e1ee --- /dev/null +++ b/', 'budget_control.cabang_id', '=', 'cabangs.id') @@ -0,0 +1,80 @@ += Illuminate\Database\Query\Builder {#6493 + +connection: Illuminate\Database\MySqlConnection {#6495}, + +grammar: Illuminate\Database\Query\Grammars\MySqlGrammar {#6477}, + +processor: Illuminate\Database\Query\Processors\MySqlProcessor {#6476}, + +bindings: [ + "select" => [], + "from" => [], + "join" => [], + "where" => [], + "groupBy" => [], + "having" => [], + "order" => [], + "union" => [], + "unionOrder" => [], + ], + +aggregate: null, + +columns: null, + +distinct: false, + +from: "budget_control", + +indexHint: null, + +joins: null, + +wheres: [], + +groups: null, + +havings: null, + +orders: null, + +limit: null, + +groupLimit: null, + +offset: null, + +unions: null, + +unionLimit: null, + +unionOffset: null, + +unionOrders: null, + +lock: null, + +beforeQueryCallbacks: [], + +operators: [ + "=", + "<", + ">", + "<=", + ">=", + "<>", + "!=", + "<=>", + "like", + "like binary", + "not like", + "ilike", + "&", + "|", + "^", + "<<", + ">>", + "&~", + "is", + "is not", + "rlike", + "not rlike", + "regexp", + "not regexp", + "~", + "~*", + "!~", + "!~*", + "similar to", + "not similar to", + "not ilike", + "~~*", + "!~~*", + ], + +bitwiseOperators: [ + "&", + "|", + "^", + "<<", + ">>", + "&~", + ], + +useWritePdo: false, + } + diff --git a/.gitignore b/.gitignore index 89636af..a7c1bd8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,8 @@ -/node_modules -/public/hot -/public/storage -/storage/*.key /vendor -.env.backup -.phpunit.result.cache -docker-compose.override.yml -Homestead.json -Homestead.yaml -npm-debug.log -yarn-error.log -/.idea -/.vscode -.env +/node_modules +/.env +/storage/framework +/storage/logs +/storage/*.key +/public/storage +*.log diff --git a/2.1 b/2.1 new file mode 100644 index 0000000..e69de29 diff --git a/BudgetControl__whereNull('region_id')-_chunkById(500, function ($budgets) use ($cabangMap) { b/BudgetControl__whereNull('region_id')-_chunkById(500, function ($budgets) use ($cabangMap) { new file mode 100644 index 0000000..36b6e80 --- /dev/null +++ b/BudgetControl__whereNull('region_id')-_chunkById(500, function ($budgets) use ($cabangMap) { @@ -0,0 +1,50 @@ += Illuminate\Support\Collection {#6508 + all: [ + 1 => 3, + 7 => 1, + 8 => 5, + 9 => 4, + 11 => 1, + 12 => 1, + 13 => 3, + 14 => 3, + 15 => 4, + 16 => 4, + 17 => 5, + 18 => 5, + 19 => 4, + 20 => 4, + 21 => 4, + 22 => 4, + 23 => 4, + 24 => 4, + 25 => 4, + 26 => 4, + 27 => 5, + 28 => 5, + 29 => 5, + 30 => 5, + 31 => 5, + 32 => 5, + 33 => 5, + 34 => 5, + 35 => 5, + 36 => 1, + 37 => 1, + 38 => 1, + 39 => 1, + 40 => 1, + 41 => 1, + 42 => 1, + 43 => 1, + 44 => 1, + 45 => 3, + 46 => 3, + 47 => 3, + 48 => 3, + 49 => 3, + 50 => 3, + 51 => 3, + ], + } + diff --git a/ample->isEmpty()) { b/ample->isEmpty()) { new file mode 100644 index 0000000..b7fc2ec --- /dev/null +++ b/ample->isEmpty()) { @@ -0,0 +1,34 @@ += Illuminate\Database\Eloquent\Collection {#8545 + all: [ + App\Models\FormUpCountry {#8380 + id: 17, + expense_number: "RG-4-BGR-UPC-25040001", + tanggal: "2025-04-29", + status: "Rejected", + }, + App\Models\FormUpCountry {#8372 + id: 18, + expense_number: "RG-3-MDO-UPC-25050001", + tanggal: "2025-05-15", + status: "Rejected", + }, + App\Models\FormUpCountry {#8381 + id: 19, + expense_number: "RG-3-MDO-UPC-25050002", + tanggal: "2025-05-16", + status: "Rejected", + }, + App\Models\FormUpCountry {#8382 + id: 20, + expense_number: "RG-3-MDO-UPC-25050003", + tanggal: "2025-05-16", + status: "Rejected", + }, + App\Models\FormUpCountry {#8383 + id: 21, + expense_number: "RG-3-MDO-UPC-25050004", + tanggal: "2025-05-22", + status: "Rejected", + }, + ], + } diff --git a/app/Console/Commands/AutoRejectExpense.php b/app/Console/Commands/AutoRejectExpense.php new file mode 100644 index 0000000..d46640f --- /dev/null +++ b/app/Console/Commands/AutoRejectExpense.php @@ -0,0 +1,97 @@ +day !== ($closingDay + 1)) { + $this->info("Hari ini bukan jadwal auto-reject. Jadwal berikutnya: Tanggal " . ($closingDay + 1)); + return; + } + + // Batas maksimal tanggal expense yang harus direject adalah H-1 dari Start Day (Misal: Tanggal 10 bulan ini) + // Karena tanggal 11 sudah masuk ke periode bulan depannya. + $batasTanggalPeriodeLalu = Carbon::create($now->year, $now->month, $startDay)->subDay()->endOfDay(); + + $models = [ + FormUpCountry::class, + FormVehicleRunningCost::class, + FormEntertaimentPresentation::class, + FormMeetingSeminar::class, + FormOthers::class + ]; + + // Daftar status yang dianggap menggantung / belum selesai + $pendingStatuses = ['On Progress', 'Approved 1', 'Approved 2', 'Waiting Approval', 'Validated']; + + $totalRejected = 0; + + DB::beginTransaction(); + try { + foreach ($models as $model) { + // Tentukan kolom tanggal yang digunakan + $dateColumn = ($model === FormMeetingSeminar::class) ? 'created_at' : 'tanggal'; + + // Ambil semua data yang masih menggantung dari periode lalu + $pendingExpenses = $model::whereIn('status', $pendingStatuses) + ->where($dateColumn, '<=', $batasTanggalPeriodeLalu) + ->get(); + + foreach ($pendingExpenses as $expense) { + // Update status dan berikan alasan penolakan sesuai instruksi core business + $expense->update([ + 'status' => 'Rejected', + 'remarks' => 'auto reject by system - lewat periode cut off', + 'rejected_at' => Carbon::now(), + 'rejected_by' => 0 // Diisi 0 sebagai flag penanda sistem otomatis + ]); + + // Tambahkan ke Audit Trail + $noExpense = $expense->expense_number ?? 'Unknown'; + AuditTrailHelper::AddAuditTrail('System Update', "Auto Reject by System for Expense ({$noExpense})"); + + $totalRejected++; + } + } + + DB::commit(); + + $msg = "Auto Reject berhasil dijalankan. Total direject: {$totalRejected} dokumen."; + $this->info($msg); + Log::info($msg); + + } catch (\Exception $e) { + DB::rollBack(); + $this->error("Gagal menjalankan Auto Reject: " . $e->getMessage()); + Log::error("Cron Auto Reject Failed: " . $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/app/Console/Commands/MigrateOldExpenseData.php b/app/Console/Commands/MigrateOldExpenseData.php new file mode 100644 index 0000000..665e3ba --- /dev/null +++ b/app/Console/Commands/MigrateOldExpenseData.php @@ -0,0 +1,111 @@ +info('Memulai Proses Sinkronisasi: Memindahkan data update dari [expense-old] ke [expense]...'); + + // Daftar tabel yang akan disinkronkan (Nama tabel diasumsikan sama pada kedua versi) + $tablesToMigrate = [ + 'form_up_country' => 'form_up_country', + 'form_vehicle_running_cost' => 'form_vehicle_running_cost', + 'form_entertainment_presentation' => 'form_entertainment_presentation', + 'form_meeting_seminar' => 'form_meeting_seminar', + 'form_others' => 'form_others', + ]; + + foreach ($tablesToMigrate as $oldTable => $newTable) { + $this->comment("--------------------------------------------------"); + $this->comment("Memproses Tabel Target: {$newTable}"); + + // 1. Validasi eksistensi tabel di database tujuan (expense) + if (!Schema::hasTable($newTable)) { + $this->error("Tabel [{$newTable}] tidak ditemukan di database 'expense'. Melewati tabel ini."); + continue; + } + + // 2. Ambil list kolom lengkap yang ada di database tujuan (expense) + $destinationColumns = Schema::getColumnListing($newTable); + + // 3. Ambil data transaksional paling update dari database lama (expense-old) + $oldData = DB::connection('mysql_lama')->table($oldTable)->get(); + + $insertedCount = 0; + $skippedCount = 0; + + foreach ($oldData as $row) { + // 4. PROTEKSI ANTI-OVERWRITE: Cek apakah data sudah ada di database 'expense' + $isExists = DB::table($newTable) + ->where('expense_number', $row->expense_number) + ->exists(); + + if (!$isExists) { + $insertData = []; + + // 5. MAPPING DATA SECARA DINAMIS + foreach ($destinationColumns as $column) { + // KUNCI PENYEMPURNAAN: Jangan bawa ID lama agar tidak tabrakan dengan Auto-Increment di DB baru + if ($column === 'id') { + continue; + } + + // Jika kolom dari database baru tersedia di data 'expense-old', ambil nilainya + if (property_exists($row, $column) && !is_null($row->$column)) { + $insertData[$column] = $row->$column; + } else { + // Jika kolom baru versi 2.0 tidak ada di database lama / bernilai NULL, isi dengan nilai default aman + $insertData[$column] = $this->getDefaultValueForColumn($newTable, $column); + } + } + + // 6. Jalankan amunisi Insert ke database utama (expense) + DB::table($newTable)->insert($insertData); + $insertedCount++; + } else { + $skippedCount++; + } + } + + $this->info("Tabel [{$newTable}] Selesai: Masuk {$insertedCount} data baru. Diabaikan (Sudah ada) {$skippedCount} data."); + } + + $this->info('=================================================='); + $this->info('Sukses! Seluruh data dari [expense-old] telah bermigrasi ke [expense] dengan aman.'); + } + + /** + * Fallback Nilai Default untuk Kolom Baru Struktur Aplikasi 2.0 + */ + private function getDefaultValueForColumn($tableName, $columnName) + { + // POINT 1: Solusi otomatisasi penanganan cabang_id yang NULL pada tabel entertainment + if ($tableName === 'form_entertainment_presentation' && $columnName === 'cabang_id') { + return 1; // Ganti angka 1 dengan ID Cabang default existing Anda + } + + // Pengondisian khusus untuk kolom-kolom baru krusial di versi 2.0 Anda + if ($columnName === 'account_number') { + return '-'; + } + if ($columnName === 'approved_total') { + return 0; + } + if ($columnName === 'approved_at' || $columnName === 'approved2_at' || $columnName === 'final_approved_at') { + return null; + } + + // Nilai bawaan jika tidak masuk kategori spesifik di atas + return null; + } +} \ No newline at end of file diff --git a/app/Exports/AuditTrailExport.php b/app/Exports/AuditTrailExport.php new file mode 100644 index 0000000..f3961d4 --- /dev/null +++ b/app/Exports/AuditTrailExport.php @@ -0,0 +1,106 @@ +filters = $filters; + } + + /** + * Menggunakan FromQuery untuk efisiensi memori pada data besar + */ + public function query() +{ + $f = $this->filters; + $query = AuditTrail::with(['user.cabang.cabang.region']); + + // Filter Region & Cabang + if (!empty($f['region']) || !empty($f['cabang'])) { + $query->whereHas('user.cabang.cabang', function($q) use ($f) { + if (!empty($f['cabang'])) { + $q->where('id', $f['cabang']); + } + if (!empty($f['region'])) { + $q->whereHas('region', function($rq) use ($f) { + // PERBAIKAN: Gunakan 'code' sesuai image_63aac6.png + $rq->where('code', $f['region']); + }); + } + }); + } + + // Filter User & Tanggal... + if (!empty($f['admin_id'])) { + $query->where('user_id', $f['admin_id']); + } + + return $query->latest(); +} + /** + * Header Tabel Excel + */ + public function headings(): array + { + return [ + 'Waktu', + 'User', + 'IP Address', + 'Aksi', + 'Aktivitas', + 'Browser' + ]; + } + + /** + * Transformasi Data per baris + */ + public function map($audit): array + { + return [ + $audit->created_at->format('d-m-Y H:i:s'), + $audit->user->name ?? 'System', + $audit->ip_address, + $audit->action, + $audit->activity, + $audit->browser, + ]; + } + + /** + * Atur Lebar Kolom agar rapi secara otomatis + */ + public function columnWidths(): array + { + return [ + 'A' => 20, + 'B' => 20, + 'C' => 15, + 'D' => 15, + 'E' => 50, + 'F' => 20, + ]; + } + + /** + * Styling Header agar lebih profesional + */ + public function styles(Worksheet $sheet) + { + return [ + 1 => ['font' => ['bold' => true]], + ]; + } +} \ No newline at end of file diff --git a/app/Exports/PendingDocumentExport.php b/app/Exports/PendingDocumentExport.php new file mode 100644 index 0000000..c6373f4 --- /dev/null +++ b/app/Exports/PendingDocumentExport.php @@ -0,0 +1,53 @@ +cabangs = $cabangs; + } + + public function collection() + { + return $this->cabangs; + } + + public function headings(): array + { + return [ + 'Region', + 'Nama Cabang', + 'Up Country', + 'Vehicle', + 'Entertainment', + 'Meeting', + 'Others', + 'Total Pending', + 'Budget Aktif' + ]; + } + + public function map($cabang): array + { + return [ + $cabang->region->name ?? '-', + $cabang->name, + $cabang->pending_details['up_country'] ?? 0, + $cabang->pending_details['vehicle'] ?? 0, + $cabang->pending_details['entertainment'] ?? 0, + $cabang->pending_details['meeting'] ?? 0, + $cabang->pending_details['others'] ?? 0, + $cabang->total_pending, + $cabang->available_budget + ]; + } +} \ No newline at end of file diff --git a/app/Helpers/AuditTrailHelper.php b/app/Helpers/AuditTrailHelper.php index 4b0ca90..3bc6ff4 100644 --- a/app/Helpers/AuditTrailHelper.php +++ b/app/Helpers/AuditTrailHelper.php @@ -4,29 +4,59 @@ namespace App\Helpers; use Illuminate\Support\Facades\Auth; use App\Models\AuditTrail; +use Illuminate\Support\Facades\Request; class AuditTrailHelper { - public static function AddAuditTrail($action, $activity) - { - $audit = new AuditTrail(); - $audit->user_id = Auth::user()->id; - $audit->ip_address = request()->ip(); - $audit->activity = $activity; - $audit->action = $action; - $audit->user_agent = request()->userAgent(); - $audit->browser = substr($audit->user_agent, 0, strpos($audit->user_agent, '/')); - $audit->save(); - } + /** + * Mencatat aktivitas umum (Insert, Update, Delete, Approve, Reject) + */ + public static function AddAuditTrail($action, $activity) + { + // Menggunakan guard 'admin' sesuai spesifikasi login aplikasi Anda + $user = Auth::guard('admin')->user(); - public static function AuthAttempt($action, $activity) - { - $audit = new AuditTrail(); - $audit->ip_address = request()->ip(); - $audit->activity = $activity; - $audit->action = $action; - $audit->user_agent = request()->userAgent(); - $audit->browser = substr($audit->user_agent, 0, strpos($audit->user_agent, '/')); - $audit->save(); - } + $audit = new AuditTrail(); + $audit->user_id = $user ? $user->id : null; + $audit->ip_address = Request::ip(); + $audit->activity = $activity; + $audit->action = $action; + $audit->user_agent = Request::userAgent(); + $audit->browser = self::getBrowserName(Request::userAgent()); + + $audit->save(); + } + + /** + * Mencatat percobaan otentikasi (Login/Logout) + */ + public static function AuthAttempt($action, $activity) + { + $audit = new AuditTrail(); + $audit->user_id = Auth::guard('admin')->id(); // Tetap coba ambil ID jika ada (misal saat logout) + $audit->ip_address = Request::ip(); + $audit->activity = $activity; + $audit->action = $action; + $audit->user_agent = Request::userAgent(); + $audit->browser = self::getBrowserName(Request::userAgent()); + + $audit->save(); + } + + /** + * Helper internal untuk deteksi browser yang lebih bersih + */ + private static function getBrowserName($userAgent) + { + if (!$userAgent) return 'Unknown'; + + if (strpos($userAgent, 'Opera') || strpos($userAgent, 'OPR')) return 'Opera'; + if (strpos($userAgent, 'Edge')) return 'Edge'; + if (strpos($userAgent, 'Chrome')) return 'Chrome'; + if (strpos($userAgent, 'Safari')) return 'Safari'; + if (strpos($userAgent, 'Firefox')) return 'Firefox'; + if (strpos($userAgent, 'MSIE') || strpos($userAgent, 'Trident/7')) return 'Internet Explorer'; + + return explode('/', $userAgent)[0] ?? 'Unknown'; + } } \ No newline at end of file diff --git a/app/Helpers/BudgetHelper.php b/app/Helpers/BudgetHelper.php index 374e6d2..a925e50 100644 --- a/app/Helpers/BudgetHelper.php +++ b/app/Helpers/BudgetHelper.php @@ -2,97 +2,344 @@ namespace App\Helpers; -use GuzzleHttp\Client; -use GuzzleHttp\Exception\RequestException; -use Illuminate\Support\Str; +use App\Models\Kategori; use App\Models\BudgetControl; use App\Models\FormUpCountry; use App\Models\FormMeetingSeminar; use App\Models\FormOthers; use App\Models\FormEntertaimentPresentation; use App\Models\FormVehicleRunningCost; -use App\Helpers\FormHelper; use App\Models\UserHasCabang; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Schema; class BudgetHelper { + /** + * Cache memori sementara untuk fungsi jurnal agar loading halaman riwayat tidak berat. + */ + private static $jurnalCache = []; + + /** + * Daftar status yang akan memotong (menge-hold) sisa budget secara real-time. + */ + const HOLD_STATUSES = [ + 'Approved 1', + 'Approved 2', + 'Waiting Approval', + 'Validated', + 'Approved', + 'Closed', + 'Completed' + ]; + + public static function getBulkAvailableBudget($budgetControls) + { + if ($budgetControls->isEmpty()) return []; + + $cabangIds = $budgetControls->pluck('cabang_id')->unique()->toArray(); + + // 1. Mapping User per Cabang + $branchUsers = UserHasCabang::whereIn('cabang_id', $cabangIds) + ->get() + ->groupBy('cabang_id') + ->map(fn($item) => $item->pluck('user_id')->toArray()); + + $allUserIds = $branchUsers->flatten()->unique()->toArray(); + $resultsMap = []; + + if (empty($allUserIds)) return []; + + $formModels = [ + 'tanggal' => [ + FormUpCountry::class, + FormVehicleRunningCost::class, + FormEntertaimentPresentation::class, + FormOthers::class + ], + 'created_at' => [ + FormMeetingSeminar::class + ] + ]; + + // 2. Tarik data pengeluaran sekaligus dari semua tabel form + foreach ($formModels as $dateColumn => $models) { + foreach ($models as $model) { + $query = $model::whereIn('user_id', $allUserIds) + ->whereIn('status', self::HOLD_STATUSES) + ->select( + 'user_id', + DB::raw("MONTH($dateColumn) as month"), + DB::raw("YEAR($dateColumn) as year"), + DB::raw("SUM(approved_total) as total") + ) + ->groupBy('user_id', 'month', 'year') + ->get(); + + // 3. Masukkan ke Map berdasarkan Cabang_Bulan_Tahun + foreach ($query as $row) { + foreach ($branchUsers as $cabangId => $userIds) { + if (in_array($row->user_id, $userIds)) { + $monthName = strtolower(date('F', mktime(0, 0, 0, $row->month, 10))); + $key = "{$cabangId}_{$monthName}_{$row->year}"; + + $resultsMap[$key] = ($resultsMap[$key] ?? 0) + $row->total; + } + } + } + } + } + + return $resultsMap; + } + + /** + * Menghitung sisa budget (Pagu - Total Hold/Terpakai). + * Tetap menggunakan kolom tanggal/created_at karena pengajuan yang baru + * dibuat (Approved 1) belum memiliki final_approved_at. + */ public static function getAvailableBudget($cabang_id, $periode_month, $periode_year) - { - // Validasi & normalisasi - $periode_month = strtolower($periode_month); - $periode_year = (int) $periode_year; + { + $periode_month = strtolower($periode_month); + $periode_year = (int) $periode_year; - // Ambil budget sesuai cabang dan periode - $budget = BudgetControl::where('cabang_id', $cabang_id) - ->where('periode_month', $periode_month) - ->where('periode_year', $periode_year) - ->with(['user.cabang']) - ->get(); + $totalPagu = BudgetControl::where('cabang_id', $cabang_id) + ->where('periode_month', $periode_month) + ->where('periode_year', $periode_year) + ->sum('amount'); - $usedBudget = 0; + if ($totalPagu <= 0) return 0; - $users = UserHasCabang::where('cabang_id', $cabang_id)->get(); - $userIds = $users->pluck('user_id')->toArray(); + $userIds = UserHasCabang::where('cabang_id', $cabang_id)->pluck('user_id')->toArray(); - $forms = FormHelper::getTotalAmount($userIds, $periode_month, $periode_year) - ->sortByDesc('tanggal') - ->sortByDesc('created_at'); + if (empty($userIds)) return $totalPagu; - $usedBudget += $forms->sum('approved_total'); + $usedBudget = 0; + $monthNumeric = date('m', strtotime($periode_month)); - $availableBudget = $budget->sum('amount') - $usedBudget; + $formModels = [ + FormUpCountry::class, + FormVehicleRunningCost::class, + FormEntertaimentPresentation::class, + FormMeetingSeminar::class, + FormOthers::class + ]; - return $availableBudget; - } + foreach ($formModels as $model) { + $query = $model::whereIn('user_id', $userIds) + ->whereIn('status', self::HOLD_STATUSES); - public static function getNominalByFormType($userIds, $type, $type_id, $month, $year) - { - if ($type == 'Up Country') { - $form = FormUpCountry::select('approved_total') - ->whereIn('user_id', $userIds) - ->whereMonth('tanggal', '=', date('m', strtotime($month))) - ->whereYear('tanggal', '=', $year) - ->whereIn('status', ['Approved', 'Closed']) - ->get(); - } elseif ($type == 'Vehicle Running Cost') { - $form = FormVehicleRunningCost::select('approved_total') - ->whereIn('user_id', $userIds) - ->whereMonth('tanggal', '=', date('m', strtotime($month))) - ->whereYear('tanggal', '=', $year) - ->whereIn('status', ['Approved', 'Closed']) - ->get(); - } elseif ($type == 'Entertainment') { - $form = FormEntertaimentPresentation::select('approved_total') - ->whereIn('user_id', $userIds) - ->whereMonth('tanggal', '=', date('m', strtotime($month))) - ->whereYear('tanggal', '=', $year) - ->whereIn('status', ['Approved', 'Closed']) - ->get(); - } elseif ($type == 'Meeting / Seminar') { - $form = FormMeetingSeminar::select('approved_total') - ->whereIn('user_id', $userIds) - ->whereMonth('created_at', '=', date('m', strtotime($month))) - ->whereYear('created_at', '=', $year) - ->whereIn('status', ['Approved', 'Closed']) - ->get(); - } else { - $form = FormOthers::select('approved_total') - ->whereIn('user_id', $userIds) - ->whereMonth('tanggal', '=', date('m', strtotime($month))) - ->whereYear('tanggal', '=', $year) - ->where('kategori_id', $type_id) - ->whereIn('status', ['Approved', 'Closed']) - ->get(); - } + if ($model === FormMeetingSeminar::class) { + $query->whereMonth('created_at', $monthNumeric) + ->whereYear('created_at', $periode_year); + } else { + $query->whereMonth('tanggal', $monthNumeric) + ->whereYear('tanggal', $periode_year); + } - return $form->sum('approved_total'); - } + $usedBudget += $query->sum('approved_total'); + } - public static function getClosingDate($cabang_id) - { - $budget = BudgetControl::where('cabang_id', $cabang_id)->orderBy('created_at', 'desc')->first(); + return $totalPagu - $usedBudget; + } - return $budget->closing_at; - } + /** + * Memvalidasi apakah tanggal expense yang diinput user berada dalam + * rentang periode aktif (Tanggal 11 s/d 10). + */ + public static function checkExpenseInputWindow($tanggal_expense) + { + $inputDate = \Carbon\Carbon::parse($tanggal_expense)->startOfDay(); + $today = \Carbon\Carbon::now(); + + $startDay = 11; + $endDay = 10; + + // Menentukan jendela periode yang sedang aktif hari ini + if ($today->day >= $startDay) { + // Jika hari ini tgl 11 atau lebih: Jendela aktif adalah Tgl 11 bulan ini s/d Tgl 10 bulan depan + $periodStart = \Carbon\Carbon::create($today->year, $today->month, $startDay)->startOfDay(); + $periodEnd = \Carbon\Carbon::create($today->year, $today->month, $endDay)->addMonth()->endOfDay(); + } else { + // Jika hari ini tgl 1-10: Jendela aktif adalah Tgl 11 bulan lalu s/d Tgl 10 bulan ini + $periodStart = \Carbon\Carbon::create($today->year, $today->month, $startDay)->subMonth()->startOfDay(); + $periodEnd = \Carbon\Carbon::create($today->year, $today->month, $endDay)->endOfDay(); + } + + // Jika tanggal form yang diinput user berada di luar jendela aktif + if ($inputDate->lt($periodStart) || $inputDate->gt($periodEnd)) { + return [ + 'is_valid' => false, + 'message' => "Gagal! Batas input expense untuk periode aktif saat ini adalah {$periodStart->format('d M Y')} s/d {$periodEnd->format('d M Y')}. Tanggal {$inputDate->format('d M Y')} sudah ditutup atau belum dibuka." + ]; + } + + return ['is_valid' => true]; + } + + /** + * Mengambil nominal per tipe form. + */ + public static function getNominalByFormType($userIds, $type, $type_id, $month, $year) + { + $monthNumeric = date('m', strtotime($month)); + $used = 0; + + if ($type == 'Up Country') { + $used = FormUpCountry::whereIn('user_id', $userIds) + ->whereMonth('tanggal', $monthNumeric)->whereYear('tanggal', $year) + ->whereIn('status', self::HOLD_STATUSES)->sum('approved_total'); + + } elseif ($type == 'Vehicle Running Cost') { + $used = FormVehicleRunningCost::whereIn('user_id', $userIds) + ->whereMonth('tanggal', $monthNumeric)->whereYear('tanggal', $year) + ->whereIn('status', self::HOLD_STATUSES)->sum('approved_total'); + + } elseif ($type == 'Entertainment') { + $used = FormEntertaimentPresentation::whereIn('user_id', $userIds) + ->whereMonth('tanggal', $monthNumeric)->whereYear('tanggal', $year) + ->whereIn('status', self::HOLD_STATUSES)->sum('approved_total'); + + } elseif ($type == 'Meeting / Seminar') { + $used = FormMeetingSeminar::whereIn('user_id', $userIds) + ->whereMonth('created_at', $monthNumeric)->whereYear('created_at', $year) + ->whereIn('status', self::HOLD_STATUSES)->sum('approved_total'); + + } else { + $used = FormOthers::whereIn('user_id', $userIds) + ->where('kategori_id', $type_id) + ->whereMonth('tanggal', $monthNumeric)->whereYear('tanggal', $year) + ->whereIn('status', self::HOLD_STATUSES)->sum('approved_total'); + } + + return $used; + } + + /** + * Mengambil tanggal closing. + */ + public static function getClosingDate($cabang_id) + { + return BudgetControl::where('cabang_id', $cabang_id) + ->orderBy('created_at', 'desc') + ->value('closing_at'); + } + + /** + * KALKULASI JURNAL AKUNTANSI + * Berlaku seragam menggunakan kolom final_approved_at untuk semua form + * Cut-off Date: Tgl 11 s/d Tgl 13 Bulan Berikutnya + */ + /** + * KALKULASI JURNAL AKUNTANSI (Log & Report) + * Sinkron dengan 12 Kategori Utama & Akun 800xxx + */ + // public static function getKalkulasiJurnal($cabang_id, $periode_month, $periode_year) + // { + // $periode_month = strtolower($periode_month); + // $periode_year = (int) $periode_year; + + // $cacheKey = "{$cabang_id}_{$periode_month}_{$periode_year}"; + // if (isset(self::$jurnalCache[$cacheKey])) return self::$jurnalCache[$cacheKey]; + + // $userIds = UserHasCabang::where('cabang_id', $cabang_id)->pluck('user_id')->toArray(); + // $totalDebet = 0; + + // if (!empty($userIds)) { + // // Ambil Range Tanggal (Contoh: 11 Maret - 13 April) + // $startDay = env('STARTING_DATE', 11); + // $closeDay = env('CLOSING_DATE', 13); + // $monthNumeric = date('m', strtotime($periode_month)); + + // $startDate = Carbon::create($periode_year, $monthNumeric, $startDay)->startOfDay(); + // $endDate = Carbon::create($periode_year, $monthNumeric, $closeDay)->addMonth()->endOfDay(); + + // // Mapping Model dengan pengamanan Typo + // $formModels = [ + // 'Up Country' => FormUpCountry::class, + // 'Vehicle Running Cost' => FormVehicleRunningCost::class, + // 'Entertainment' => FormEntertaimentPresentation::class, + // 'Entertaiment' => FormEntertaimentPresentation::class, + // 'Presentation' => FormEntertaimentPresentation::class, + // 'Meeting / Seminar' => FormMeetingSeminar::class, + // ]; + + // // Ambil kategori yang punya account_number dan tidak dikecualikan + // $kategoris = \App\Models\Kategori::whereNotIn('name', ['Cash in Bank BCA', 'Donasi/Gift', 'PPn Masukan', 'Sponsorship']) + // ->whereNotNull('account_number') + // ->where('account_number', '!=', '') + // ->get(); + + // foreach ($kategoris as $kat) { + // $katName = trim($kat->name); + + // if (isset($formModels[$katName])) { + // $model = $formModels[$katName]; + // $query = $model::whereIn('user_id', $userIds) + // ->whereIn('status', ['Closed', 'Final Approved']) + // ->whereBetween('final_approved_at', [$startDate, $endDate]); + + // // PENTING: Filter berdasarkan kategori_id agar data tidak tertukar di tabel yang sama + // if (\Illuminate\Support\Facades\Schema::hasColumn((new $model)->getTable(), 'kategori_id')) { + // $query->where('kategori_id', $kat->id); + // } + // $totalDebet += $query->sum('approved_total'); + // } else { + // // Masuk ke Form Others + // $totalDebet += FormOthers::whereIn('user_id', $userIds) + // ->where('kategori_id', $kat->id) + // ->whereIn('status', ['Closed', 'Final Approved']) + // ->whereBetween('final_approved_at', [$startDate, $endDate]) + // ->sum('approved_total'); + // } + // } + // } + + // self::$jurnalCache[$cacheKey] = $totalDebet; + // return $totalDebet; + // } + + public static function getBulkKalkulasiJurnal(array $lookups) +{ + if (empty($lookups)) return []; + + $resultsMap = []; + $startDay = 11; // Batas awal periode (Contoh: tgl 11) + $closeDay = 13; // Batas akhir approval (Contoh: tgl 13 bulan depan) + + foreach ($lookups as $item) { + $cabId = $item['cabang_id']; + $monthNumeric = date('m', strtotime($item['month'])); + $year = (int) $item['year']; + $key = $cabId . '_' . strtolower($item['month']) . '_' . $year; + + // Penentuan Rentang Tanggal Cut-off Akuntansi + $startDate = \Illuminate\Support\Carbon::create($year, $monthNumeric, $startDay)->startOfDay(); + $endDate = $startDate->copy()->addMonth()->day($closeDay)->endOfDay(); + + $userIds = \App\Models\UserHasCabang::where('cabang_id', $cabId)->pluck('user_id')->toArray(); + + if (empty($userIds)) { + $resultsMap[$key] = 0; + continue; + } + + // Menghitung Total Pengeluaran Riil (Status: Closed / Final Approved) + $tables = ['form_up_country', 'form_vehicle_running_cost', 'form_entertainment_presentation', 'form_meeting_seminar', 'form_others']; + $totalExpense = 0; + + foreach ($tables as $table) { + $totalExpense += \Illuminate\Support\Facades\DB::table($table) + ->whereIn('user_id', $userIds) + ->whereIn('status', ['Closed', 'Final Approved']) + ->whereBetween('final_approved_at', [$startDate, $endDate]) + ->sum('approved_total'); + } + + $resultsMap[$key] = $totalExpense; + } + + return $resultsMap; } +} \ No newline at end of file diff --git a/app/Helpers/FormHelper-old.php b/app/Helpers/FormHelper-old.php new file mode 100644 index 0000000..878501f --- /dev/null +++ b/app/Helpers/FormHelper-old.php @@ -0,0 +1,366 @@ +whereIn('id', $userIds) + ->get(); + + $formUpCountryQuery = FormUpCountry::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Up Country' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); + + $formVehicleRunningCostQuery = FormVehicleRunningCost::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Vehicle Running Cost' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); + + $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Entertainment Presentation' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); + + $formMeetingSeminarQuery = FormMeetingSeminar::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Meeting Seminar' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('created_at', [$startDate, $closingDateNextMonth]); + + $formOthersQuery = FormOthers::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Others' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); + + // Use union to combine queries + $forms = $formUpCountryQuery + ->union($formVehicleRunningCostQuery) + ->union($formEntertaimentPresentationQuery) + ->union($formMeetingSeminarQuery) + ->union($formOthersQuery) + ->get(); + + // Return forms with eager loaded user details + return $forms->map(function ($form) use ($users) { + $user = $users->firstWhere('id', $form->user_id); + $form->user = $user; + return $form; + }); + } + + public static function getAllForms($month, $year) { + $monthNumber = date('m', strtotime($month . ' 1')); + $startDate = "$year-$monthNumber-" . env('STARTING_DATE'); + + $nextMonthTimestamp = strtotime("first day of next month", strtotime("$year-$monthNumber-01")); + $closingDateNextMonth = date('Y-m-', $nextMonthTimestamp) . env('CLOSING_DATE'); + + $users = Admin::with(['region', 'cabang']) + ->get(); + + $formUpCountryQuery = FormUpCountry::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Up Country' as type") + )->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); + + $formVehicleRunningCostQuery = FormVehicleRunningCost::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Vehicle Running Cost' as type") + )->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); + + $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Entertainment Presentation' as type") + )->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); + + $formMeetingSeminarQuery = FormMeetingSeminar::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Meeting Seminar' as type") + )->whereBetween('created_at', [$startDate, $closingDateNextMonth]); + + $formOthersQuery = FormOthers::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Others' as type") + )->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); + + // Combine queries using union + $forms = $formUpCountryQuery + ->union($formVehicleRunningCostQuery) + ->union($formEntertaimentPresentationQuery) + ->union($formMeetingSeminarQuery) + ->union($formOthersQuery) + ->get(); + + // Map users to the forms with eager loaded data + return $forms->map(function ($form) use ($users) { + $user = $users->firstWhere('id', $form->user_id); + $form->user = $user; + return $form; + }); + } + + public static function getAllForms2() { + // Get all users with eager loading for 'region' and 'cabang' + $users = Admin::with(['region', 'cabang']) + ->get(); + + $formUpCountryQuery = FormUpCountry::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Up Country' as type") + ); + + $formVehicleRunningCostQuery = FormVehicleRunningCost::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Vehicle Running Cost' as type") + ); + + $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Entertainment Presentation' as type") + ); + + $formMeetingSeminarQuery = FormMeetingSeminar::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Meeting Seminar' as type") + ); + + $formOthersQuery = FormOthers::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Others' as type") + ); + + // Combine queries using union + $forms = $formUpCountryQuery + ->union($formVehicleRunningCostQuery) + ->union($formEntertaimentPresentationQuery) + ->union($formMeetingSeminarQuery) + ->union($formOthersQuery) + ->get(); + + // Map users to the forms with eager loaded data + return $forms->map(function ($form) use ($users) { + $user = $users->firstWhere('id', $form->user_id); + $form->user = $user; + return $form; + }); + } + + public static function getNominalByFormType($cabang_id, $type, $type_id, $month, $year) { + $users = UserHasCabang::where('cabang_id', $cabang_id)->get(); + $userIds = $users->pluck('user_id')->toArray(); + + if($type == 'Up Country') { + $form = FormUpCountry::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('tanggal', '=', date('m', strtotime($month)))->whereYear('tanggal', '=', $year)->where('status', ['Approved', 'Closed'])->get(); + } else if($type == 'Vehicle Running Cost') { + $form = FormVehicleRunningCost::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('tanggal', '=', date('m', strtotime($month)))->whereYear('tanggal', '=', $year)->where('status', ['Approved', 'Closed'])->get(); + } else if($type == 'Entertainment') { + $form = FormEntertaimentPresentation::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('tanggal', '=', date('m', strtotime($month)))->whereYear('tanggal', '=', $year)->where('status', ['Approved', 'Closed'])->get(); + } else if($type == 'Meeting / Seminar') { + $form = FormMeetingSeminar::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('created_at', '=', date('m', strtotime($month)))->whereYear('created_at', '=', $year)->where('status', ['Approved', 'Closed'])->get(); + } else { + $form = FormOthers::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('tanggal', '=', date('m', strtotime($month)))->whereYear('tanggal', '=', $year)->where('kategori_id', $type_id)->where('status', ['Approved', 'Closed'])->get(); + } + + $nominal = $form->sum('approved_total'); + return $nominal; + } + + public static function getTotalAmount($userIds, $month, $year) { + $monthNumber = date('m', strtotime($month . ' 1')); + $startDate = "$year-$monthNumber-" . env('STARTING_DATE'); + + $nextMonthTimestamp = strtotime("first day of next month", strtotime("$year-$monthNumber-01")); + $closingDateNextMonth = date('Y-m-', $nextMonthTimestamp) . env('CLOSING_DATE'); + + $users = Admin::with(['region', 'cabang']) + ->whereIn('id', $userIds) + ->get(); + + $formUpCountryQuery = FormUpCountry::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Up Country' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); + + $formVehicleRunningCostQuery = FormVehicleRunningCost::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Vehicle Running Cost' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); + + $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Entertainment Presentation' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); + + $formMeetingSeminarQuery = FormMeetingSeminar::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Meeting Seminar' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); + + $formOthersQuery = FormOthers::select( + 'expense_number', + 'user_id', + 'total', + 'approved_total', + 'status', + 'account_number', + 'created_at', + DB::raw("'Others' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); + + // Use union to combine queries + $forms = $formUpCountryQuery + ->union($formVehicleRunningCostQuery) + ->union($formEntertaimentPresentationQuery) + ->union($formMeetingSeminarQuery) + ->union($formOthersQuery) + ->get(); + + // Return forms with eager loaded user details + return $forms->map(function ($form) use ($users) { + $user = $users->firstWhere('id', $form->user_id); + $form->user = $user; + return $form; + }); + } +} diff --git a/app/Helpers/FormHelper.php b/app/Helpers/FormHelper.php index 878501f..c715b8b 100644 --- a/app/Helpers/FormHelper.php +++ b/app/Helpers/FormHelper.php @@ -10,357 +10,203 @@ use App\Models\FormUpCountry; use App\Models\FormVehicleRunningCost; use Illuminate\Support\Facades\DB; use App\Models\UserHasCabang; +use Illuminate\Support\Carbon; class FormHelper { - public static function getFormsByUserIds($userIds, $month, $year) { - $monthNumber = date('m', strtotime($month . ' 1')); - $startDate = "$year-$monthNumber-" . env('STARTING_DATE'); + /** + * Helper internal untuk menghitung rentang siklus tanggal + * Menghasilkan: Start (11), Max Input (10 bln depan), Closing (13 bln depan) + */ + private static function getCycleRange($month, $year) +{ + // Pastikan di-cast ke (int) agar Carbon tidak error + $startDay = (int) env('STARTING_DATE', 11); + $maxInputDay = (int) env('INPUT_MAX_DATE', 10); + $closingDay = (int) env('CLOSING_DATE', 13); - $nextMonthTimestamp = strtotime("first day of next month", strtotime("$year-$monthNumber-01")); - $closingDateNextMonth = date('Y-m-', $nextMonthTimestamp) . env('CLOSING_DATE'); + // Mengambil nomor bulan sebagai integer + $monthNumber = (int) date('m', strtotime($month . ' 1')); + + // Pastikan $year juga integer + $year = (int) $year; - $users = Admin::with(['region', 'cabang']) - ->whereIn('id', $userIds) - ->get(); + // Carbon::createFromDate membutuhkan integer + $startDate = \Illuminate\Support\Carbon::createFromDate($year, $monthNumber, $startDay)->startOfDay(); + + // Input maksimal dan Closing menggunakan method ->day() yang butuh integer + $maxInputDate = $startDate->copy()->addMonth()->day($maxInputDay)->endOfDay(); + $closingDate = $startDate->copy()->addMonth()->day($closingDay)->endOfDay(); - $formUpCountryQuery = FormUpCountry::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Up Country' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); - - $formVehicleRunningCostQuery = FormVehicleRunningCost::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Vehicle Running Cost' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); - - $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Entertainment Presentation' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); - - $formMeetingSeminarQuery = FormMeetingSeminar::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Meeting Seminar' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('created_at', [$startDate, $closingDateNextMonth]); - - $formOthersQuery = FormOthers::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Others' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); - - // Use union to combine queries - $forms = $formUpCountryQuery - ->union($formVehicleRunningCostQuery) - ->union($formEntertaimentPresentationQuery) - ->union($formMeetingSeminarQuery) - ->union($formOthersQuery) - ->get(); - - // Return forms with eager loaded user details - return $forms->map(function ($form) use ($users) { - $user = $users->firstWhere('id', $form->user_id); - $form->user = $user; - return $form; - }); - } - - public static function getAllForms($month, $year) { - $monthNumber = date('m', strtotime($month . ' 1')); - $startDate = "$year-$monthNumber-" . env('STARTING_DATE'); - - $nextMonthTimestamp = strtotime("first day of next month", strtotime("$year-$monthNumber-01")); - $closingDateNextMonth = date('Y-m-', $nextMonthTimestamp) . env('CLOSING_DATE'); - - $users = Admin::with(['region', 'cabang']) - ->get(); - - $formUpCountryQuery = FormUpCountry::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Up Country' as type") - )->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); - - $formVehicleRunningCostQuery = FormVehicleRunningCost::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Vehicle Running Cost' as type") - )->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); - - $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Entertainment Presentation' as type") - )->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); - - $formMeetingSeminarQuery = FormMeetingSeminar::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Meeting Seminar' as type") - )->whereBetween('created_at', [$startDate, $closingDateNextMonth]); - - $formOthersQuery = FormOthers::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Others' as type") - )->whereBetween('tanggal', [$startDate, $closingDateNextMonth]); - - // Combine queries using union - $forms = $formUpCountryQuery - ->union($formVehicleRunningCostQuery) - ->union($formEntertaimentPresentationQuery) - ->union($formMeetingSeminarQuery) - ->union($formOthersQuery) - ->get(); - - // Map users to the forms with eager loaded data - return $forms->map(function ($form) use ($users) { - $user = $users->firstWhere('id', $form->user_id); - $form->user = $user; - return $form; - }); - } - - public static function getAllForms2() { - // Get all users with eager loading for 'region' and 'cabang' - $users = Admin::with(['region', 'cabang']) - ->get(); - - $formUpCountryQuery = FormUpCountry::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Up Country' as type") - ); - - $formVehicleRunningCostQuery = FormVehicleRunningCost::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Vehicle Running Cost' as type") - ); - - $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Entertainment Presentation' as type") - ); - - $formMeetingSeminarQuery = FormMeetingSeminar::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Meeting Seminar' as type") - ); - - $formOthersQuery = FormOthers::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Others' as type") - ); - - // Combine queries using union - $forms = $formUpCountryQuery - ->union($formVehicleRunningCostQuery) - ->union($formEntertaimentPresentationQuery) - ->union($formMeetingSeminarQuery) - ->union($formOthersQuery) - ->get(); - - // Map users to the forms with eager loaded data - return $forms->map(function ($form) use ($users) { - $user = $users->firstWhere('id', $form->user_id); - $form->user = $user; - return $form; - }); - } - - public static function getNominalByFormType($cabang_id, $type, $type_id, $month, $year) { - $users = UserHasCabang::where('cabang_id', $cabang_id)->get(); - $userIds = $users->pluck('user_id')->toArray(); - - if($type == 'Up Country') { - $form = FormUpCountry::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('tanggal', '=', date('m', strtotime($month)))->whereYear('tanggal', '=', $year)->where('status', ['Approved', 'Closed'])->get(); - } else if($type == 'Vehicle Running Cost') { - $form = FormVehicleRunningCost::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('tanggal', '=', date('m', strtotime($month)))->whereYear('tanggal', '=', $year)->where('status', ['Approved', 'Closed'])->get(); - } else if($type == 'Entertainment') { - $form = FormEntertaimentPresentation::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('tanggal', '=', date('m', strtotime($month)))->whereYear('tanggal', '=', $year)->where('status', ['Approved', 'Closed'])->get(); - } else if($type == 'Meeting / Seminar') { - $form = FormMeetingSeminar::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('created_at', '=', date('m', strtotime($month)))->whereYear('created_at', '=', $year)->where('status', ['Approved', 'Closed'])->get(); - } else { - $form = FormOthers::select('approved_total')->whereIn('user_id', $userIds)->whereMonth('tanggal', '=', date('m', strtotime($month)))->whereYear('tanggal', '=', $year)->where('kategori_id', $type_id)->where('status', ['Approved', 'Closed'])->get(); - } - - $nominal = $form->sum('approved_total'); - return $nominal; - } - - public static function getTotalAmount($userIds, $month, $year) { - $monthNumber = date('m', strtotime($month . ' 1')); - $startDate = "$year-$monthNumber-" . env('STARTING_DATE'); - - $nextMonthTimestamp = strtotime("first day of next month", strtotime("$year-$monthNumber-01")); - $closingDateNextMonth = date('Y-m-', $nextMonthTimestamp) . env('CLOSING_DATE'); - - $users = Admin::with(['region', 'cabang']) - ->whereIn('id', $userIds) - ->get(); - - $formUpCountryQuery = FormUpCountry::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Up Country' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); - - $formVehicleRunningCostQuery = FormVehicleRunningCost::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Vehicle Running Cost' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); - - $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Entertainment Presentation' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); - - $formMeetingSeminarQuery = FormMeetingSeminar::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Meeting Seminar' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); - - $formOthersQuery = FormOthers::select( - 'expense_number', - 'user_id', - 'total', - 'approved_total', - 'status', - 'account_number', - 'created_at', - DB::raw("'Others' as type") - )->whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $closingDateNextMonth]); - - // Use union to combine queries - $forms = $formUpCountryQuery - ->union($formVehicleRunningCostQuery) - ->union($formEntertaimentPresentationQuery) - ->union($formMeetingSeminarQuery) - ->union($formOthersQuery) - ->get(); - - // Return forms with eager loaded user details - return $forms->map(function ($form) use ($users) { - $user = $users->firstWhere('id', $form->user_id); - $form->user = $user; - return $form; - }); - } + return [ + 'start' => $startDate->toDateTimeString(), + 'input_max' => $maxInputDate->toDateTimeString(), + 'closing' => $closingDate->toDateTimeString(), + 'display_label' => $startDate->format('d M') . ' - ' . $maxInputDate->format('d M Y') + ]; } + + public static function getFormsByUserIds($userIds, $month, $year) { + $dates = self::getCycleRange($month, $year); + $startDate = $dates['start']; + $closingDate = $dates['closing']; // Mengacu ke Closing Date (13) + + $users = Admin::with(['region', 'cabang']) + ->whereIn('id', $userIds) + ->get(); + + $formUpCountryQuery = FormUpCountry::select( + 'expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', + DB::raw("'Up Country' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('tanggal', [$startDate, $closingDate]); + + $formVehicleRunningCostQuery = FormVehicleRunningCost::select( + 'expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', + DB::raw("'Vehicle Running Cost' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('tanggal', [$startDate, $closingDate]); + + $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select( + 'expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', + DB::raw("'Entertainment Presentation' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('tanggal', [$startDate, $closingDate]); + + $formMeetingSeminarQuery = FormMeetingSeminar::select( + 'expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', + DB::raw("'Meeting Seminar' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('created_at', [$startDate, $closingDate]); + + $formOthersQuery = FormOthers::select( + 'expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', + DB::raw("'Others' as type") + )->whereIn('user_id', $userIds) + ->whereBetween('tanggal', [$startDate, $closingDate]); + + $forms = $formUpCountryQuery + ->union($formVehicleRunningCostQuery) + ->union($formEntertaimentPresentationQuery) + ->union($formMeetingSeminarQuery) + ->union($formOthersQuery) + ->get(); + + return $forms->map(function ($form) use ($users) { + $user = $users->firstWhere('id', $form->user_id); + $form->user = $user; + return $form; + }); + } + + public static function getAllForms($month, $year) { + $dates = self::getCycleRange($month, $year); + $startDate = $dates['start']; + $closingDate = $dates['closing']; + + $users = Admin::with(['region', 'cabang'])->get(); + + $commonSelect = ['expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at']; + + $queries = [ + FormUpCountry::select(array_merge($commonSelect, [DB::raw("'Up Country' as type")]))->whereBetween('tanggal', [$startDate, $closingDate]), + FormVehicleRunningCost::select(array_merge($commonSelect, [DB::raw("'Vehicle Running Cost' as type")]))->whereBetween('tanggal', [$startDate, $closingDate]), + FormEntertaimentPresentation::select(array_merge($commonSelect, [DB::raw("'Entertainment Presentation' as type")]))->whereBetween('tanggal', [$startDate, $closingDate]), + FormMeetingSeminar::select(array_merge($commonSelect, [DB::raw("'Meeting Seminar' as type")]))->whereBetween('created_at', [$startDate, $closingDate]), + FormOthers::select(array_merge($commonSelect, [DB::raw("'Others' as type")]))->whereBetween('tanggal', [$startDate, $closingDate]), + ]; + + $mainQuery = array_shift($queries); + foreach ($queries as $q) { $mainQuery->union($q); } + + $forms = $mainQuery->get(); + + return $forms->map(function ($form) use ($users) { + $user = $users->firstWhere('id', $form->user_id); + $form->user = $user; + return $form; + }); + } + + /** + * Dashboard Info: Menghasilkan label periode input (11-10) + */ + public static function getDashboardPeriodLabel($month, $year) { + return self::getCycleRange($month, $year)['display_label']; + } + + public static function getNominalByFormType($cabang_id, $type, $type_id, $month, $year) { + $users = UserHasCabang::where('cabang_id', $cabang_id)->get(); + $userIds = $users->pluck('user_id')->toArray(); + + // Menggunakan logika cycle agar nominal yang muncul sinkron dengan cutoff + $dates = self::getCycleRange($month, $year); + $start = $dates['start']; + $end = $dates['closing']; + + $query = null; + if($type == 'Up Country') { + $query = FormUpCountry::whereIn('user_id', $userIds)->whereBetween('tanggal', [$start, $end]); + } else if($type == 'Vehicle Running Cost') { + $query = FormVehicleRunningCost::whereIn('user_id', $userIds)->whereBetween('tanggal', [$start, $end]); + } else if($type == 'Entertainment') { + $query = FormEntertaimentPresentation::whereIn('user_id', $userIds)->whereBetween('tanggal', [$start, $end]); + } else if($type == 'Meeting / Seminar') { + $query = FormMeetingSeminar::whereIn('user_id', $userIds)->whereBetween('created_at', [$start, $end]); + } else { + $query = FormOthers::whereIn('user_id', $userIds)->whereBetween('tanggal', [$start, $end])->where('kategori_id', $type_id); + } + + return $query->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'); + } + + public static function getTotalAmount($userIds, $month, $year) { + $dates = self::getCycleRange($month, $year); + $startDate = $dates['start']; + $closingDate = $dates['closing']; + + $users = Admin::with(['region', 'cabang'])->whereIn('id', $userIds)->get(); + + $formUpCountryQuery = FormUpCountry::select('expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', DB::raw("'Up Country' as type")) + ->whereIn('user_id', $userIds)->whereBetween('final_approved_at', [$startDate, $closingDate]); + + $formVehicleRunningCostQuery = FormVehicleRunningCost::select('expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', DB::raw("'Vehicle Running Cost' as type")) + ->whereIn('user_id', $userIds)->whereBetween('final_approved_at', [$startDate, $closingDate]); + + $formEntertaimentPresentationQuery = FormEntertaimentPresentation::select('expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', DB::raw("'Entertainment Presentation' as type")) + ->whereIn('user_id', $userIds)->whereBetween('final_approved_at', [$startDate, $closingDate]); + + $formMeetingSeminarQuery = FormMeetingSeminar::select('expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', DB::raw("'Meeting Seminar' as type")) + ->whereIn('user_id', $userIds)->whereBetween('final_approved_at', [$startDate, $closingDate]); + + $formOthersQuery = FormOthers::select('expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at', DB::raw("'Others' as type")) + ->whereIn('user_id', $userIds)->whereBetween('final_approved_at', [$startDate, $closingDate]); + + $forms = $formUpCountryQuery->union($formVehicleRunningCostQuery)->union($formEntertaimentPresentationQuery)->union($formMeetingSeminarQuery)->union($formOthersQuery)->get(); + + return $forms->map(function ($form) use ($users) { + $user = $users->firstWhere('id', $form->user_id); + $form->user = $user; + return $form; + }); + } + + public static function getAllForms2() { + // Fungsi ini tetap dipertahankan sesuai request (tanpa filter tanggal) + $users = Admin::with(['region', 'cabang'])->get(); + $commonSelect = ['expense_number', 'user_id', 'total', 'approved_total', 'status', 'account_number', 'created_at']; + + $forms = FormUpCountry::select(array_merge($commonSelect, [DB::raw("'Up Country' as type")])) + ->union(FormVehicleRunningCost::select(array_merge($commonSelect, [DB::raw("'Vehicle Running Cost' as type")]))) + ->union(FormEntertaimentPresentation::select(array_merge($commonSelect, [DB::raw("'Entertainment Presentation' as type")]))) + ->union(FormMeetingSeminar::select(array_merge($commonSelect, [DB::raw("'Meeting Seminar' as type")]))) + ->union(FormOthers::select(array_merge($commonSelect, [DB::raw("'Others' as type")]))) + ->get(); + + return $forms->map(function ($form) use ($users) { + $user = $users->firstWhere('id', $form->user_id); + $form->user = $user; + return $form; + }); + } +} \ No newline at end of file diff --git a/app/Helpers/PeriodeHelper.php b/app/Helpers/PeriodeHelper.php index e4c1870..425358a 100644 --- a/app/Helpers/PeriodeHelper.php +++ b/app/Helpers/PeriodeHelper.php @@ -1,19 +1,23 @@ Start: 11 April 2026, End: 13 Mei 2026 (Closing Date) + */ + public static function getPeriodeRange(string $month, int $year): array + { + // 1. Tentukan tanggal 11 di bulan dan tahun yang dipilih + $startDate = Carbon::parse("11 $month $year")->startOfDay()->toDateTimeString(); - // Jika Januari, periode dimulai dari 11 Januari hingga 10 Februari - $start = Carbon::create($year, $month, 11)->startOfDay(); - $end = $start->copy()->addMonth()->subDay()->endOfDay(); + // 2. Tentukan tanggal 13 di bulan berikutnya (Bulan berjalan + 1) + $endDate = Carbon::parse("13 $month $year")->addMonth()->endOfDay()->toDateTimeString(); - return [$start, $end]; - } -} + return [$startDate, $endDate]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Backend/AuditTrailController.php b/app/Http/Controllers/Backend/AuditTrailController.php index ad0b56f..06f9e83 100644 --- a/app/Http/Controllers/Backend/AuditTrailController.php +++ b/app/Http/Controllers/Backend/AuditTrailController.php @@ -6,26 +6,108 @@ namespace App\Http\Controllers\Backend; use App\Http\Controllers\Controller; use App\Models\AuditTrail; -use Spatie\Permission\Models\Permission; -use Spatie\Permission\Models\Role; +use App\Models\Admin; // Pastikan Model Admin diimport +use App\Models\Region; // Pastikan Model Region diimport +use App\Models\Cabang; // Pastikan Model Cabang diimport +use App\Helpers\AuditTrailHelper; +use Illuminate\Http\Request; // BARIS INI YANG WAJIB ADA +use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\DB; +use App\Exports\AuditTrailExport; +use Maatwebsite\Excel\Facades\Excel; class AuditTrailController extends Controller { - public function index() - { - $pageInfo = [ - 'title' => 'Audit Trails', - 'sub_title' => 'List of Audit Trails System', - 'path' => url()->current() - ]; + public function index(Request $request) +{ + $pageInfo = [ + 'title' => 'Audit Trails', + 'sub_title' => 'List of Audit Trails System', + 'path' => url()->current() + ]; - return view( - 'backend.pages.audit_trail.index', - [ - // sort by newest - 'data' => AuditTrail::with('user')->orderBy('created_at', 'desc')->get(), - 'pageInfo' => $pageInfo, - ] - ); + $query = AuditTrail::with(['user.cabang.cabang.region']); + + // Filter: User + if ($request->filled('admin_id')) { + $query->where('user_id', $request->admin_id); } + + // Filter: Region & Cabang (Sesuai relasi hasOne UserHasCabang) + if ($request->filled('region') || $request->filled('cabang')) { + $query->whereHas('user.cabang.cabang', function($q) use ($request) { + if ($request->filled('cabang')) { + $q->where('id', $request->cabang); + } + if ($request->filled('region')) { + $q->whereHas('region', function($rq) use ($request) { + // PERBAIKAN: Gunakan 'code' sesuai image_63aac6.png + $rq->where('code', $request->region); + }); + } + }); + } + + // Filter: Rentang Waktu + if ($request->filled('start_date') && $request->filled('end_date')) { + $query->whereBetween('created_at', [ + $request->start_date . ' 00:00:00', + $request->end_date . ' 23:59:59' + ]); + } + + $data = $query->latest()->paginate(50)->appends($request->all()); + + return view('backend.pages.audit_trail.index', [ + 'data' => $data, + 'pageInfo' => $pageInfo, + 'admins' => \App\Models\Admin::select('id', 'name')->orderBy('name')->get(), + // PERBAIKAN: Select 'code' sesuai image_63aac6.png + 'regions' => \App\Models\Region::select('code', 'name')->get(), + 'cabangs' => \App\Models\Cabang::select('id', 'name')->orderBy('name')->get(), + ]); } + +public function exportExcel(Request $request) +{ + AuditTrailHelper::AddAuditTrail('Export', 'Melakukan export data Audit Trail dengan filter tertentu'); + + // Mengirimkan parameter filter ke class Export + return Excel::download(new AuditTrailExport($request->all()), 'Audit_Trail_' . date('Y-m-d') . '.xlsx'); +} + + + + /** + * Membersihkan semua data audit trail (Truncate) + */ + public function clearAll() + { + try { + // Catat siapa yang menghapus log sebelum data dihapus + AuditTrailHelper::AddAuditTrail('Delete', 'Membersihkan (Truncate) seluruh data Audit Trail'); + + // Menggunakan truncate untuk performa maksimal pada tabel besar + DB::table('audit_trail')->truncate(); + + return redirect()->back()->with('success', 'All audit logs have been cleared.'); + } catch (\Exception $e) { + return redirect()->back()->with('error', 'Failed to clear logs: ' . $e->getMessage()); + } + } + + /** + * Fungsi untuk membersihkan cache sistem + */ + public function clearCache() + { + try { + Artisan::call('optimize:clear'); + AuditTrailHelper::AddAuditTrail('System', 'Membersihkan cache sistem (Optimize Clear)'); + + return redirect()->back()->with('success', 'System cache cleared successfully!'); + } catch (\Exception $e) { + return redirect()->back()->with('error', 'Failed to clear cache: ' . $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Backend/BudgetControlController.php b/app/Http/Controllers/Backend/BudgetControlController.php index 8cde750..0424064 100644 --- a/app/Http/Controllers/Backend/BudgetControlController.php +++ b/app/Http/Controllers/Backend/BudgetControlController.php @@ -11,164 +11,617 @@ use App\Models\BudgetControlLog; use App\Models\Admin; use Illuminate\Http\Request; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; + +use Maatwebsite\Excel\Facades\Excel; +use Maatwebsite\Excel\Concerns\FromArray; +use Maatwebsite\Excel\Concerns\WithHeadings; +use App\Helpers\PeriodeHelper; class BudgetControlController extends Controller { - public function index() - { - $this->checkAuthorization(auth()->user(), ['budget_control.view']); - session()->put('redirect_url', route('budget_control')); + /** + * Halaman Utama Budget Control (Termasuk Data Grafik) + */ + public function index(Request $request) +{ + $this->checkAuthorization(auth()->user(), ['budget_control.view']); + + $user = auth()->user(); + $role = $user->getRoleNames()->first(); - $role = auth()->user()->getRoleNames()[0]; - $budget = null; + // --- 1. IDENTIFIKASI WILAYAH USER (Sama seperti Dashboard) --- + // Mengambil semua cabang yang di-assign ke user ini + $userCabangIds = \App\Models\UserHasCabang::where('user_id', $user->id) + ->pluck('cabang_id') + ->toArray(); - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - $region_id = auth()->user()->getMyCabangAndRegionId()['region']; + // Mengambil semua ID Region dari cabang-cabang tersebut + $userRegionIds = \App\Models\Cabang::whereIn('id', $userCabangIds) + ->pluck('region_id') + ->unique() + ->toArray(); - if ($region_id) { - $budget = BudgetControl::whereHas('cabang', function ($query) use ($region_id) { - $query->where('region_id', $region_id); - })->with(['receiver', 'sender', 'cabang'])->get(); - } - } elseif ($role == 'Area Manager Cabang') { - $cabang_id = auth()->user()->getMyCabangAndRegionId()['cabang']; - - if ($cabang_id) { - $budget = BudgetControl::where('cabang_id', $cabang_id) - ->with(['receiver', 'sender', 'cabang']) - ->get(); - } - } else { - $budget = BudgetControl::with(['receiver', 'sender', 'cabang'])->get(); - } - - // Tentukan periode berdasarkan start dan closing date - $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); - $now = Carbon::now(); - - if ($now->day >= $startDay) { - $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); - } else { - $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); - } - - // Hitung available budget berdasarkan periode - $budget->map(function ($b) { - $b->availableBudget = BudgetHelper::getAvailableBudget($b->cabang_id, $b->periode_month, $b->periode_year); - return $b; - }); - - return view('backend.pages.budget_control.index', [ - 'data' => $budget, - ]); - } - - public function log() - { - $this->checkAuthorization(auth()->user(), ['budget_control.view']); - session()->put('redirect_url', route('budget_control')); - - return view( - 'backend.pages.budget_control.log', - [ - 'data' => BudgetControlLog::orderBy('created_at', 'desc')->with(['receiver', 'sender', 'cabang'])->get(), - ] - ); + // --- 2. KOLEKSI DROPDOWN FILTER BERDASARKAN ROLE --- + if (in_array($role, ['Admin Region', 'Marketing Operational Manager Region'])) { + // Dropdown Region HANYA muncul region miliknya (Menghilangkan Blank) + $regions = \App\Models\Region::whereIn('id', $userRegionIds)->get(); + // Dropdown Cabang HANYA muncul cabang-cabang di bawah region tersebut + $cabangOptions = \App\Models\Cabang::whereIn('region_id', $userRegionIds)->get(); + + } elseif (in_array($role, ['Area Manager Cabang', 'Medical Representatif'])) { + $regions = \App\Models\Region::whereIn('id', $userRegionIds)->get(); + $cabangOptions = \App\Models\Cabang::whereIn('id', $userCabangIds)->get(); + + } else { + // Superadmin & Accounting: Muncul Semua + $regions = \App\Models\Region::all(); + $cabangOptions = \App\Models\Cabang::when($request->region_id, function($q) use ($request) { + return $q->where('region_id', $request->region_id); + })->get(); } - public function create() - { - $this->checkAuthorization(auth()->user(), ['budget_control.create']); + // --- 3. QUERY UTAMA DENGAN PROTEKSI DATA --- + $query = \App\Models\BudgetControl::with(['receiver', 'sender', 'cabang.region']); - $role = auth()->user()->getRoleNames()[0]; + // Jika bukan Superadmin/Accounting, kunci kueri ke wilayahnya saja + if (!in_array($role, ['Superadmin', 'Super Admin', 'Accounting', 'accounting'])) { + $query->whereHas('cabang', function($q) use ($userRegionIds) { + $q->whereIn('region_id', $userRegionIds); + }); + } - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - $region_id = auth()->user()->getMyCabangAndRegionId()['region']; + // --- 4. FILTER DARI INPUT USER --- + if ($request->filled('region_id')) { + $query->whereHas('cabang', fn($q) => $q->where('region_id', $request->region_id)); + } + if ($request->filled('cabang_id')) { + $query->where('cabang_id', $request->cabang_id); + } + if ($request->filled('month')) { + $query->where('periode_month', strtolower($request->month)); + } + if ($request->filled('year')) { + $query->where('periode_year', $request->year); + } - if ($region_id) { - // only show users from the same region - $users = Admin::where('id', '!=', auth()->user()->id) - ->whereHas('cabang', function ($query) use ($region_id) { - $query->where('region_id', $region_id); - }) - ->get(); - } else { - $users = Admin::getUserByRoleName('Area Manager Cabang'); - } - } elseif ($role == 'Area Manager Cabang') { - $cabang_id = auth()->user()->getMyCabangAndRegionId()['cabang']; + $data = $query->latest()->paginate(50)->withQueryString(); - if ($cabang_id) { - // only show users from the same cabang - $users = Admin::where('id', '!=', auth()->user()->id) - ->whereHas('cabang', function ($query) use ($cabang_id) { - $query->where('id', $cabang_id); - }) - ->get(); - } - } else { - $users = Admin::getUserByRoleName('Area Manager Cabang'); - } + // --- 5. OPTIMASI PERHITUNGAN REALISASI --- + $expenseMap = \App\Helpers\BudgetHelper::getBulkAvailableBudget($data); - return view('backend.pages.budget_control.create', [ - 'users' => $users, - ]); - } + $data->getCollection()->transform(function ($b) use ($expenseMap) { + $key = $b->cabang_id . '_' . strtolower($b->periode_month) . '_' . $b->periode_year; + $b->availableBudget = isset($expenseMap[$key]) ? ($b->amount - $expenseMap[$key]) : $b->amount; + return $b; + }); - public function store(Request $request) - { - $this->checkAuthorization(auth()->user(), ['budget_control.create']); - - $validated = $request->validate([ - 'receiver_id' => 'required|exists:admins,id', - 'periode_month' => 'required|string|in:january,february,march,april,may,june,july,august,september,october,november,december', - 'periode_year' => 'required|numeric', - 'amount' => 'required|numeric', - 'remarks' => 'nullable|string', - ]); - - $cabang_id = Admin::find($validated['receiver_id'])->getMyCabangAndRegionId()['cabang']; - $periode = [ - 'periode_month' => $validated['periode_month'], - 'periode_year' => $validated['periode_year'], - ]; - - BudgetControl::updateOrCreate( - array_merge(['cabang_id' => $cabang_id], $periode), - array_merge($periode, [ - 'sender_id' => auth()->user()->id, - 'receiver_id' => $validated['receiver_id'], - 'amount' => $validated['amount'], - 'remarks' => $validated['remarks'], - 'closing_at' => null, - 'status' => 'Assigned', - ]) - ); - - BudgetControlLog::create([ - 'cabang_id' => $cabang_id, - 'sender_id' => auth()->user()->id, - 'receiver_id' => $validated['receiver_id'], - 'periode_month' => $validated['periode_month'], - 'periode_year' => $validated['periode_year'], - 'amount' => $validated['amount'], - 'remarks' => $validated['remarks'], - 'closing_at' => null, - 'status' => 'Assigned', - ]); - - session()->flash('success', 'Budget has been added or updated successfully'); - return redirect()->route('budget_control'); - } - - public function destroy($id) - { - $this->checkAuthorization(auth()->user(), ['budget_control.delete']); - - $budgetRequest = BudgetControl::findOrfail($id); - $budgetRequest->delete(); - - return redirect()->route('budget_control'); - } + return view('backend.pages.budget_control.index', compact('data', 'regions', 'cabangOptions')); } + /** + * Menampilkan Log Riwayat Budget (Disempurnakan dengan Total, Realisasi, DR, CR) + */ + /** + * Menampilkan Log Riwayat Budget (Menampilkan Budget & Realisasi Cash BCA) + */ + /** + * Menampilkan Log Riwayat Budget dengan Integrasi Tabel Transaksi + */ + public function log(Request $request) +{ + $this->checkAuthorization(auth()->user(), ['budget_control.view']); + + // Data untuk Dropdown Filter + $regions = \App\Models\Region::all(); + $cabangOptions = \App\Models\Cabang::when($request->region_id, function($q) use ($request) { + return $q->where('region_id', $request->region_id); + })->get(); + + // Query Utama dengan Filter Dinamis + $query = \App\Models\BudgetControlLog::with(['receiver', 'sender', 'cabang.region']); + + if ($request->filled('region_id')) { + $query->whereHas('cabang', fn($q) => $q->where('region_id', $request->region_id)); + } + if ($request->filled('cabang_id')) { + $query->where('cabang_id', $request->cabang_id); + } + if ($request->filled('month')) { + $query->where('periode_month', strtolower($request->month)); + } + if ($request->filled('year')) { + $query->where('periode_year', $request->year); + } + + $data = $query->orderBy('created_at', 'desc')->paginate(50)->withQueryString(); + + // Sinkronisasi Kalkulasi Realisasi + $lookups = $data->map(fn($l) => [ + 'cabang_id' => $l->cabang_id, + 'month' => $l->periode_month, + 'year' => $l->periode_year + ])->unique()->toArray(); + + $debetMap = \App\Helpers\BudgetHelper::getBulkKalkulasiJurnal($lookups); + + $data->getCollection()->transform(function ($log) use ($debetMap) { + $key = $log->cabang_id . '_' . strtolower($log->periode_month) . '_' . $log->periode_year; + + $log->total_budget = (float) $log->amount; + $log->total_credit = (float) $log->amount; // Pagu/Transfer + $log->total_debet = (float) ($debetMap[$key] ?? 0); // Pengeluaran riil + $log->realisasi = $log->total_credit - $log->total_debet; // Sisa Saldo + + return $log; + }); + + // Menyiapkan Data Grafik (Top 10 hasil filter) + $chartData = [ + 'labels' => $data->getCollection()->take(10)->map(fn($l) => $l->cabang->name)->toArray(), + 'budget' => $data->getCollection()->take(10)->pluck('total_budget')->toArray(), + 'realisasi' => $data->getCollection()->take(10)->pluck('realisasi')->toArray(), + ]; + + return view('backend.pages.budget_control.log', compact('data', 'regions', 'cabangOptions', 'chartData')); +} + +private function prepareLogChartData($collection) +{ + // Mengelompokkan data berdasarkan periode untuk grafik + $grouped = $collection->groupBy(function($item) { + return strtoupper($item->periode_month) . ' ' . $item->periode_year; + })->take(6); // Ambil 6 periode terbaru yang ada di list + + return [ + 'labels' => $grouped->keys()->toArray(), + 'budget' => $grouped->map(fn($group) => $group->sum('total_budget'))->values()->toArray(), + 'realisasi' => $grouped->map(fn($group) => $group->sum('realisasi'))->values()->toArray(), + ]; +} + + /** + * Download Template untuk Bulk Upload + */ + public function downloadTemplate() + { + $fileName = 'Template_Bulk_Upload_Budget.csv'; + + $headers = [ + "Content-type" => "text/csv", + "Content-Disposition" => "attachment; filename=$fileName", + "Pragma" => "no-cache", + "Cache-Control" => "must-revalidate, post-check=0, pre-check=0", + "Expires" => "0" + ]; + + $columns = ['Kode Cabang', 'Bulan', 'Nominal Budget', 'Keterangan']; + + $callback = function() use($columns) { + $file = fopen('php://output', 'w'); + + // Baris 1: Header + fputcsv($file, $columns); + + // Baris 2 & 3: Contoh data agar user paham + fputcsv($file, ['JKT-01', 'January', '15000000', 'Budget Ops JKT-01']); + fputcsv($file, ['BDG-01', 'January', '12500000', 'Budget Ops BDG-01']); + + fclose($file); + }; + + return response()->stream($callback, 200, $headers); + } + + public static function generateRefNumber($cabang) +{ + // Mengambil 3 huruf kode cabang (Pastikan Anda menggunakan kolom yang tepat, misalnya $cabang->code atau $cabang->name) + $kodeCabang = strtoupper(substr($cabang->code ?? $cabang->name, 0, 3)); + + // Format ym (Contoh: 2605 untuk Mei 2026) + $ym = date('ym'); + + $prefix = "JRN-{$kodeCabang}-{$ym}"; + + // Menghitung urutan transaksi untuk cabang dan bulan ini + $count = \Illuminate\Support\Facades\DB::table('transactions') + ->where('cabang_id', $cabang->id) + ->whereRaw("DATE_FORMAT(created_at, '%y%m') = ?", [$ym]) + ->count(); + + $sequence = str_pad((string)($count + 1), 3, '0', STR_PAD_LEFT); + + // Hasil akhir: JRN-PDG-2605-001 + return "{$prefix}-{$sequence}"; +} + /** + * Form Create Budget + */ + public function create() + { + $this->checkAuthorization(auth()->user(), ['budget_control.create']); + + $user = auth()->user(); + $role = $user->getRoleNames()[0]; + + if (in_array($role, ['Admin Region', 'Marketing Operational Manager Region'])) { + $region_id = $user->getMyCabangAndRegionId()['region']; + $users = $region_id + ? Admin::where('id', '!=', $user->id)->whereHas('cabang', fn($q) => $q->where('region_id', $region_id))->get() + : Admin::getUserByRoleName('Area Manager Cabang'); + } elseif ($role == 'Area Manager Cabang') { + $cabang_id = $user->getMyCabangAndRegionId()['cabang']; + $users = $cabang_id + ? Admin::where('id', '!=', $user->id)->whereHas('cabang', fn($q) => $q->where('id', $cabang_id))->get() + : collect(); + } else { + $users = Admin::getUserByRoleName('Area Manager Cabang'); + } + + return view('backend.pages.budget_control.create', ['users' => $users]); + } + /** + * Menampilkan Halaman Filter Generate Report Jurnal + */ + public function jurnal() + { + $user = auth()->user(); + $role = $user->getRoleNames()[0] ?? null; + $userData = $user->getMyCabangAndRegionId(); + + $regions = Region::all(); + $cabangs = Cabang::all(); + + // Filter data berdasarkan hak akses (Role) + if (in_array($role, ['Admin Region', 'Marketing Operational Manager Region'])) { + $regions = Region::where('id', $userData['region'])->get(); + $cabangs = Cabang::where('region_id', $userData['region'])->get(); + } elseif ($role == 'Area Manager Cabang') { + $cabangs = Cabang::where('id', $userData['cabang'])->get(); + $regions = Region::where('id', $cabangs->first()->region_id ?? 0)->get(); + } + + return view('backend.pages.report.jurnal', [ + 'pageInfo' => ['title' => 'Generate Reports For Jurnal'], + 'cabang' => $cabangs, + 'regions' => $regions + ]); + } + + /** + * Proses Menghitung Data Jurnal (Debet, Kredit, & Realisasi) + */ + public function generateJurnal() + { + $params = request()->all(); + $accounting_period = $params['accounting_period'] ?? '-'; + + // 1. Ambil kategori yang valid (Wajib punya Account Number & tidak di-exclude) + $excluded = ['Cash in Bank BCA', 'Donasi/Gift', 'PPn Masukan', 'Vehicle Maintenance', 'Bahan Promosi', 'Sponsorship']; + $kategoris = Kategori::whereNotIn('name', $excluded) + ->whereNotNull('account_number') + ->where('account_number', '!=', '') + ->where('account_number', '!=', '0') + ->get(); + + $kategori_bca = Kategori::where('name', 'Cash in Bank BCA')->first(); + $cabang = Cabang::where('code', $params['cabang'])->firstOrFail(); + $year = $params['year']; + $month = $params['month']; + + // 2. Ambil Range Tanggal Cut-off (Misal: 11 Maret - 13 April) + [$startDate, $endDate] = PeriodeHelper::getPeriodeRange($month, $year); + + // 3. KREDIT: Total Petty Cash yang diterima cabang (Pagu) + $pettyCashAmount = BudgetControl::where('cabang_id', $cabang->id) + ->where('periode_year', $year) + ->where('periode_month', $month) + ->sum('amount'); + + // Log budget untuk display di view + $budget = BudgetControlLog::where('cabang_id', $cabang->id) + ->where('periode_year', $year) + ->where('periode_month', $month) + ->orderByDesc('created_at')->first(); + + $userIds = UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id'); + + // Mapping Model & Alias + $formModels = [ + 'Up Country' => FormUpCountry::class, + 'Vehicle Running Cost' => FormVehicleRunningCost::class, + 'Entertainment' => FormEntertaimentPresentation::class, + 'Entertaiment' => FormEntertaimentPresentation::class, + 'Presentation' => FormEntertaimentPresentation::class, + 'Meeting / Seminar' => FormMeetingSeminar::class, + ]; + + $nominals = []; + foreach ($kategoris as $kategori) { + $sum = 0; + $katName = trim($kategori->name); + + if (isset($formModels[$katName])) { + $model = $formModels[$katName]; + $sum = $model::whereIn('user_id', $userIds) + ->where('expense_number', 'LIKE', "%-{$cabang->code}-%") // Filter Cabang + ->where('kategori_id', $kategori->id) // Filter Kategori WAJIB + ->whereIn('status', ['Closed', 'Final Approved']) + ->whereBetween('final_approved_at', [$startDate, $endDate]) + ->sum('approved_total'); + } else { + $sum = FormOthers::whereIn('user_id', $userIds) + ->where('expense_number', 'LIKE', "%-{$cabang->code}-%") // Filter Cabang + ->where('kategori_id', $kategori->id) // Filter Kategori WAJIB + ->whereIn('status', ['Closed', 'Final Approved']) + ->whereBetween('final_approved_at', [$startDate, $endDate]) + ->sum('approved_total'); + } + $nominals[$kategori->id] = $sum; +} + $totalDebet = array_sum($nominals); // Total Pengeluaran riil + + return view('backend.pages.report.generate_jurnal', [ + 'pageInfo' => ['title' => 'Generate Reports For Jurnal'], + 'kategori' => $kategoris, + 'cabang' => $cabang, + 'year' => $year, + 'month' => $month, + 'kategori_bca' => $kategori_bca, + 'nominals' => $nominals, + 'budget' => $budget, + 'totalKategori' => $totalDebet, // Total Debet Jurnal + 'pettyCashAmount' => $pettyCashAmount, // Total Kredit Jurnal + 'cashInBca' => $pettyCashAmount - $totalDebet, // Sisa Uang BCA + 'accounting_period' => $accounting_period, + ]); + } + + /** + * Simpan Budget Baru & Log + */ + public function store(Request $request) + { + $this->checkAuthorization(auth()->user(), ['budget_control.create']); + $validated = $request->validate([ + 'receiver_id' => 'required|exists:admins,id', + 'periode_month' => 'required|string', + 'periode_year' => 'required|numeric', + 'amount' => 'required|numeric', + 'remarks' => 'nullable|string', + ]); + + return DB::transaction(function () use ($validated) { + $receiver = Admin::find($validated['receiver_id']); + $cabang_id = $receiver->getMyCabangAndRegionId()['cabang']; + + $existing = BudgetControl::where('cabang_id', $cabang_id) + ->where('periode_month', $validated['periode_month']) + ->where('periode_year', $validated['periode_year']) + ->first(); + + if ($existing && strtolower($existing->status) == 'assigned') { + return back()->with('error', 'Gagal: Budget periode ini sudah Assigned.'); + } + + $budgetData = [ + 'sender_id' => auth()->user()->id, + 'receiver_id' => $validated['receiver_id'], + 'amount' => $validated['amount'], + 'remarks' => $validated['remarks'], + 'status' => 'Assigned', + ]; + + BudgetControl::updateOrCreate( + ['cabang_id' => $cabang_id, 'periode_month' => $validated['periode_month'], 'periode_year' => $validated['periode_year']], + $budgetData + ); + + BudgetControlLog::create(array_merge(['cabang_id' => $cabang_id], $validated, $budgetData)); + + return redirect()->route('budget_control')->with('success', 'Budget Assigned successfully'); + }); + } + + /** + * Proses Upload Bukti Transfer (Disempurnakan untuk dukung PDF & Gambar) + */ + public function uploadProof(Request $request) + { + // PERBAIKAN: Ganti 'image' menjadi 'file', karena aturan 'image' akan menolak PDF + $request->validate([ + 'budget_id' => 'required|exists:budget_control,id', + 'transfer_proof' => 'required|file|mimes:jpeg,png,jpg,pdf|max:5120', // Maks 5MB + ], [ + 'transfer_proof.mimes' => 'Format file harus berupa JPG, JPEG, PNG, atau PDF.', + 'transfer_proof.max' => 'Ukuran file maksimal 5MB.' + ]); + + $budget = BudgetControl::find($request->budget_id); + + if ($request->hasFile('transfer_proof')) { + $file = $request->file('transfer_proof'); + $filename = time() . '_' . str_replace(' ', '_', $file->getClientOriginalName()); + + // Simpan file + $file->move(public_path('uploads/transfer_proofs'), $filename); + + $budget->update([ + 'transfer_proof' => 'uploads/transfer_proofs/' . $filename, + 'transfer_status' => 'Waiting Approval' + ]); + + return back()->with('success', 'Bukti transfer berhasil diunggah.'); + } + + return back()->with('error', 'File gagal diunggah.'); + } + + /** + * Konfirmasi Accounting (Validated) + */ + public function approveTransfer($id) + { + BudgetControl::where('id', $id)->update([ + 'transfer_status' => 'Validated', + 'updated_at' => now() + ]); + return back()->with('success', 'Transfer divalidasi.'); + } + + /** + * Penolakan Accounting (Rejected) + */ + public function rejectTransfer($id) + { + BudgetControl::where('id', $id)->update([ + 'transfer_status' => 'Rejected', + 'updated_at' => now() + ]); + return back()->with('error', 'Transfer ditolak.'); + } + + /** + * Proses Bulk Upload Budget via Excel/CSV + */ + public function bulkUpload(Request $request) + { + $this->checkAuthorization(auth()->user(), ['budget_control.create']); + + // Tambahkan 'csv' pada mimes + $request->validate([ + 'upload_type' => 'required|in:region,cabang', + 'periode_year' => 'required|numeric', + 'file_excel' => 'required|file|mimes:xlsx,xls,csv|max:5120', + ], [ + 'file_excel.mimes' => 'Format file wajib berupa Microsoft Excel (.xls / .xlsx) atau CSV.' + ]); + + // 1. Tentukan Cabang ID mana saja yang diizinkan untuk di-upload pada proses ini + $cabangIds = []; + if ($request->upload_type === 'region') { + $request->validate(['region_id' => 'required|exists:region,id']); + $cabangIds = \App\Models\Cabang::where('region_id', $request->region_id)->pluck('id')->toArray(); + } else { + $request->validate(['cabang_id' => 'required|array', 'cabang_id.*' => 'exists:cabang,id']); + $cabangIds = $request->cabang_id; + } + + try { + // 2. Baca file Excel menggunakan Maatwebsite (otomatis jadi Array) + // Parameter pertama new \stdClass() digunakan sebagai dummy import class + $data = Excel::toArray(new \stdClass(), $request->file('file_excel')); + $rows = $data[0] ?? []; + + DB::beginTransaction(); + $count = 0; + + // 3. Looping data Excel + foreach ($rows as $index => $row) { + // Skip baris pertama (Asumsi baris 1 adalah Header) + if ($index === 0) continue; + + $cabangCode = $row[0] ?? null; + $month = $row[1] ?? null; + $amount = (float) ($row[2] ?? 0); + $remarks = $row[3] ?? null; + + if (!$cabangCode || !$month || $amount <= 0) continue; + + // Cari cabang berdasarkan Kode Cabang di Excel + $cabang = \App\Models\Cabang::where('code', $cabangCode)->first(); + + // Validasi Keamanan: Pastikan cabang ditemukan DAN termasuk dalam scope filter modal + if (!$cabang || !in_array($cabang->id, $cabangIds)) continue; + + $budgetData = [ + 'sender_id' => auth()->user()->id, + 'receiver_id' => $cabang->head_id ?? auth()->user()->id, // Assign ke Head Cabang + 'amount' => $amount, + 'remarks' => $remarks, + 'status' => 'Assigned', + ]; + + // Simpan atau Update Budget + BudgetControl::updateOrCreate( + [ + 'cabang_id' => $cabang->id, + 'periode_month' => $month, + 'periode_year' => $request->periode_year + ], + $budgetData + ); + + // Catat di Log + BudgetControlLog::create(array_merge([ + 'cabang_id' => $cabang->id, + 'periode_month' => $month, + 'periode_year' => $request->periode_year + ], $budgetData)); + + $count++; + } + + DB::commit(); + return back()->with('success', "Bulk upload berhasil! Sebanyak {$count} data budget telah diproses."); + + } catch (\Exception $e) { + DB::rollBack(); + \Illuminate\Support\Facades\Log::error("Bulk Upload Error: " . $e->getMessage()); + return back()->with('error', 'Gagal memproses file Excel. Pastikan format tabel sudah sesuai.'); + } + } + + /** + * Hapus Data Budget + */ + public function destroy($id) + { + $this->checkAuthorization(auth()->user(), ['budget_control.delete']); + BudgetControl::findOrFail($id)->delete(); + return redirect()->route('budget_control')->with('success', 'Deleted successfully'); + } + + /** + * PRIVATE METHOD: Logika Grafik Trend Petty Cash vs Realisasi + */ + private function getTrendChartData(array $cabangIds): array + { + $currentYear = \Carbon\Carbon::now()->year; + $months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + $pettyCashData = []; + $realisasiData = []; + + foreach ($months as $month) { + $pettyCash = \App\Models\BudgetControl::whereIn('cabang_id', $cabangIds) + ->where('periode_month', strtolower($month)) + ->where('periode_year', $currentYear) + ->sum('amount'); + + // Realisasi Sementara Di-set 0 + $realisasi = 0; + + $pettyCashData[] = (float) $pettyCash; + $realisasiData[] = (float) $realisasi; + } + + return [ + 'labels' => $months, + 'datasets' => [ + [ + 'label' => 'Petty Cash (Budget)', + 'data' => $pettyCashData, + 'backgroundColor' => 'rgba(54, 162, 235, 0.6)', + 'borderColor' => 'rgba(54, 162, 235, 1)', + ], + [ + 'label' => 'Realisasi', + 'data' => $realisasiData, + 'backgroundColor' => 'rgba(255, 99, 132, 0.6)', + 'borderColor' => 'rgba(255, 99, 132, 1)', + ] + ] + ]; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Backend/DashboardController.php b/app/Http/Controllers/Backend/DashboardController.php index 9c80af6..a8b19e7 100644 --- a/app/Http/Controllers/Backend/DashboardController.php +++ b/app/Http/Controllers/Backend/DashboardController.php @@ -5,120 +5,243 @@ declare(strict_types=1); namespace App\Http\Controllers\Backend; use App\Http\Controllers\Controller; -use App\Models\Admin; -use Spatie\Permission\Models\Permission; -use Spatie\Permission\Models\Role; -use App\Models\FormUpCountry; -use App\Models\FormEntertaimentPresentation; -use App\Models\FormVehicleRunningCost; -use App\Models\FormMeetingSeminar; -use App\Models\FormOthers; -use App\Models\UserHasCabang; -use App\Models\Notifications; +use App\Models\Region; +use App\Models\Cabang; +use App\Models\Kategori; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; use Carbon\Carbon; class DashboardController extends Controller { - public function index() - { - $role = auth()->user()->getRoleNames()[0]; - $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + private array $tables = [ + 'up_country' => 'form_up_country', + 'vehicle' => 'form_vehicle_running_cost', + 'entertainment' => 'form_entertainment_presentation', + 'meeting' => 'form_meeting_seminar', + 'others' => 'form_others' + ]; - // Referensi waktu sekarang + public function index(Request $request) + { + $user = auth()->user(); + $roleName = trim($user->getRoleNames()->first() ?? ''); $now = Carbon::now(); - // Tentukan periode - if ($now->day >= $startDay) { - // Mulai dari tanggal START bulan ini - $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); - // Sampai tanggal CLOSING bulan depan - $closingDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $closingDay)->endOfDay(); - } else { - // Mulai dari tanggal START bulan lalu - $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); - // Sampai tanggal CLOSING bulan ini - $closingDate = Carbon::create($now->year, $now->month, $closingDay)->endOfDay(); + // 1. FILTER WAKTU (Default ke bulan berjalan) + $chartMonth = $request->chart_month ?? $now->format('m'); + $chartYear = $request->chart_year ?? $now->format('Y'); + $monthName = Carbon::createFromFormat('m', $chartMonth)->translatedFormat('F'); + + // --- KALKULASI PERIODE PENILAIAN (11 di bulan terpilih s/d 13 di bulan berikutnya) --- + $displayStart = Carbon::create((int)$chartYear, (int)$chartMonth, 11)->startOfDay(); + $displayEnd = $displayStart->copy()->addMonth()->day(13)->endOfDay(); // Dikunci aman ke tanggal 13 penutupan closing laporan + + $periodeLabelFormatted = $displayStart->translatedFormat('d F') . ' - ' . $displayEnd->translatedFormat('d F Y'); + + // 2. FILTER OTORISASI & DATA + $allowedCabangIds = $this->getScopedCabangIds($user, $roleName); + if (empty($allowedCabangIds)) { + return view('backend.pages.dashboard.index', $this->emptyData()); } - $formQueries = [ - 'vehicle' => FormVehicleRunningCost::query() - ->whereMonth('tanggal', date('m')) - ->whereBetween('tanggal', [$startDate, $closingDate]), - 'upcountry' => FormUpCountry::query() - ->whereMonth('tanggal', date('m')) - ->whereBetween('tanggal', [$startDate, $closingDate]), - 'entertainment' => FormEntertaimentPresentation::query() - ->whereMonth('tanggal', date('m')) - ->whereBetween('tanggal', [$startDate, $closingDate]), - 'meeting' => FormMeetingSeminar::query() - ->whereMonth('created_at', date('m')) - ->whereBetween('created_at', [$startDate, $closingDate]), - 'others' => FormOthers::query() - ->whereMonth('tanggal', date('m')) - ->whereBetween('tanggal', [$startDate, $closingDate]), + $availableRegions = $this->getAvailableRegions($user, $roleName, $allowedCabangIds); + $cabangOptions = Cabang::whereIn('id', $allowedCabangIds) + ->when($request->region_id, fn($q) => $q->where('region_id', $request->region_id)) + ->get(); + + // 3. AMBIL DATA PENDING & PERFORMA (Passing Variable Waktu Siklus ke Chart Method) + $cabangDetails = $this->getPendingExpenseTable($allowedCabangIds, $request); + $performance = $this->getPerformanceCharts($allowedCabangIds, $displayStart, $displayEnd); + $totalKategori = Kategori::whereNotNull('account_number')->where('account_number', '!=', '')->count(); + + $data = [ + 'periodeLabel' => $periodeLabelFormatted, + 'totalRegions' => Region::count(), + 'totalCabangs' => count($allowedCabangIds), + 'totalKategori' => $totalKategori, + 'regions' => $availableRegions, + 'cabangOptions' => $cabangOptions, + 'cabangDetails' => $cabangDetails, + 'pendingCount' => $this->getTotalPendingGlobal($allowedCabangIds), ]; - $approvedStatuses = [ - // 'Approved', - // 'Approved 1', - // 'Approved 2', - // 'Final Approved', - // 'Final Approve', - 'Closed', - ]; + return view('backend.pages.dashboard.index', array_merge($data, $performance)); + } - $forms = []; + // ======================================================================== + // PRIVATE METHODS - SEPARATION OF CONCERNS + // ======================================================================== - foreach ($formQueries as $key => $query) { - $forms[$key] = $query - ->whereIn('status', $approvedStatuses) - ->whereNotNull('final_approved_at') - ->sum('approved_total'); + /** + * MENGAMBIL DATA UNTUK TABEL MONITORING PENDING + */ + private function getPendingExpenseTable($allowedCabangIds, Request $request) + { + $query = Cabang::whereIn('id', $allowedCabangIds)->with('region'); + + if ($request->region_id) $query->where('region_id', $request->region_id); + if ($request->cabang_id) $query->where('id', $request->cabang_id); + if ($request->filled('search')) $query->where('name', 'like', "%{$request->search}%"); + + $sortField = $request->get('sort', 'name'); + $sortOrder = $request->get('direction', 'asc'); + + if ($sortField == 'region') { + $query->leftJoin('region', 'cabang.region_id', '=', 'region.id') + ->select('cabang.*')->orderBy('region.name', $sortOrder); + } else { + $query->orderBy('cabang.' . ($sortField == 'total_pending' ? 'name' : $sortField), $sortOrder); } - return view( - 'backend.pages.dashboard.index', - [ - 'forms' => $forms, - ] - ); + $cabangDetails = $query->paginate(100)->withQueryString()->through(function($cabang) { + $statuses = ['Pending', 'On Progress', 'Approved 1', 'Approved 2', 'Waiting Approval']; + $details = []; $total = 0; + + foreach ($this->tables as $key => $table) { + $count = DB::table($table)->where('cabang_id', $cabang->id)->whereIn('status', $statuses)->count(); + $details[$key] = $count; + $total += $count; + } + + $cabang->pending_details = $details; + $cabang->total_pending = $total; + return $cabang; + }); + + if ($sortField == 'total_pending') { + $sortedCollection = $cabangDetails->getCollection()->sortBy('total_pending', SORT_REGULAR, $sortOrder == 'desc')->values(); + $cabangDetails->setCollection($sortedCollection); + } + + return $cabangDetails; } - public function notifications() + /** + * MENGHITUNG PERFORMA KECEPATAN APPROVE CABANG (Fokus Akurat Jendela Siklus Transaksi Tanggal 11 - 13) + */ + private function getPerformanceCharts($cabangIds, Carbon $startDate, Carbon $endDate): array { - $this->checkAuthorization(auth()->user(), ['notification.view']); + $queryUnion = null; + + foreach ($this->tables as $table) { + $q = DB::table($table) + ->select('cabang_id', 'created_at', 'final_approved_at', 'updated_at') + ->where('status', 'Closed') + ->whereIn('cabang_id', $cabangIds) + // KUNCI UTAMA: Hanya membaca pengajuan yang dibuat murni di dalam rentang tanggal siklus 11 s/d 13 berjalan + ->whereBetween('tanggal', [$startDate->format('Y-m-d H:i:s'), $endDate->format('Y-m-d H:i:s')]); + + $queryUnion = is_null($queryUnion) ? $q : $queryUnion->unionAll($q); + } - // Mulai dengan query dasar - $notifications = Notifications::orderBy('created_at', 'desc') - ->where('receiver_id', auth()->user()->id) - ->where('is_read', 0); + if ($queryUnion && DB::table(DB::raw("({$queryUnion->toSql()}) as t"))->mergeBindings($queryUnion)->exists()) { + // Kalkulasi selisih waktu rata-rata penyelesaian berkas menggunakan fondasi TIMESTAMPDIFF detik secara objektif + $res = DB::table(DB::raw("({$queryUnion->toSql()}) as combined")) + ->mergeBindings($queryUnion) + ->select('cabang_id', DB::raw('AVG(TIMESTAMPDIFF(SECOND, created_at, COALESCE(final_approved_at, updated_at))) as avg_sec')) + ->groupBy('cabang_id') + ->get(); - // Ambil data dan kembalikan sebagai JSON - return response()->json([ - 'count' => $notifications->count(), - 'data' => $notifications->take(7)->get(), - ]); + $cabangNames = Cabang::whereIn('id', $res->pluck('cabang_id'))->pluck('name', 'id'); + $fastest = $res->sortBy('avg_sec')->take(5); + $slowest = $res->sortByDesc('avg_sec')->take(5); + + return [ + 'chartFastest' => [ + 'labels' => $fastest->map(fn($i) => $cabangNames[$i->cabang_id] ?? 'N/A')->values(), + 'data' => $fastest->map(fn($i) => round($i->avg_sec / 3600, 2))->values(), + 'raw' => $fastest->map(fn($i) => $this->formatDuration((int)$i->avg_sec))->values() + ], + 'chartSlowest' => [ + 'labels' => $slowest->map(fn($i) => $cabangNames[$i->cabang_id] ?? 'N/A')->values(), + 'data' => $slowest->map(fn($i) => round($i->avg_sec / 3600, 2))->values(), + 'raw' => $slowest->map(fn($i) => $this->formatDuration((int)$i->avg_sec))->values() + ] + ]; + } + + return [ + 'chartFastest' => ['labels'=>[], 'data'=>[], 'raw'=>[]], + 'chartSlowest' => ['labels'=>[], 'data'=>[], 'raw'=>[]] + ]; } - public function mark_read($id) + /** + * GET AVAILABLE REGIONS BERDASARKAN ROLE + */ + private function getAvailableRegions($user, $roleName, $allowedCabangIds) { - $this->checkAuthorization(auth()->user(), ['notification.delete']); - $receiver_id = auth()->user()->id; - - Notifications::where('receiver_id', $receiver_id)->where('id', $id)->update(['is_read' => 1]); - - return response()->json(['message' => 'Notification marked as read.']); + $regionsQuery = Region::query(); + if (in_array($roleName, ['Admin Region', 'Marketing Operational Manager Region'])) { + $regionsQuery->where('id', $user->region_id); + } elseif (in_array($roleName, ['Area Manager Cabang', 'Medical Representatif'])) { + $myRegionIds = Cabang::whereIn('id', $allowedCabangIds)->pluck('region_id')->unique(); + $regionsQuery->whereIn('id', $myRegionIds); + } + return $regionsQuery->get(); } - public function mark_all_read() + private function getScopedCabangIds($user, $roleName): array { - $this->checkAuthorization(auth()->user(), ['notification.delete']); - $receiver_id = auth()->user()->id; + $central = ['Superadmin', 'Head of Sales Marketing', 'Marketing Information System', 'Accounting']; + if (in_array($roleName, $central)) return Cabang::pluck('id')->toArray(); - Notifications::where('receiver_id', $receiver_id)->update(['is_read' => 1]); + if (in_array($roleName, ['Admin Region', 'Marketing Operational Manager Region'])) { + return $user->region_id ? Cabang::where('region_id', $user->region_id)->pluck('id')->toArray() : []; + } - session()->flash('success', 'All notifications marked as read.'); - return redirect()->back(); + if (in_array($roleName, ['Area Manager Cabang', 'Medical Representatif'])) { + $cabangId = $user->cabang->cabang_id ?? \App\Models\Rayon::where('id', $user->rayon_id)->value('cabang_id'); + return $cabangId ? [(int)$cabangId] : []; + } + return []; } -} + + private function getTotalPendingGlobal($cabangIds): int + { + $total = 0; + foreach ($this->tables as $table) { + $total += DB::table($table)->whereIn('cabang_id', $cabangIds)->whereIn('status', ['Pending', 'On Progress', 'Approved 1', 'Approved 2', 'Waiting Approval'])->count(); + } + return $total; + } + + private function formatDuration(int $sec): string + { + if ($sec <= 0) return "0 Jam"; + $d = floor($sec / 86400); $h = floor(($sec % 86400) / 3600); + return $d > 0 ? "{$d} Hari {$h} Jam" : "{$h} Jam"; + } + + private function emptyData(): array + { + return [ + 'periodeLabel' => '-', 'totalRegions' => 0, 'totalCabangs' => 0, 'totalKategori' => 0, 'pendingCount' => 0, + 'cabangDetails' => collect(), 'chartFastest' => ['labels'=>[], 'data'=>[], 'raw'=>[]], + 'chartSlowest' => ['labels'=>[], 'data'=>[], 'raw'=>[]], 'regions' => collect(), 'cabangOptions' => collect() + ]; + } + + public function notifications() { return response()->json(['status' => 'success', 'data' => []]); } + + public function show($id) + { + try { + $user = auth()->user(); + $allowedIds = $this->getScopedCabangIds($user, $user->getRoleNames()->first()); + if (!in_array((int)$id, $allowedIds)) abort(403); + + $cabang = Cabang::with('region')->findOrFail($id); + $statuses = ['Pending', 'On Progress', 'Approved 1', 'Approved 2', 'Waiting Approval']; + + $data = []; + foreach (['Up Country' => 'form_up_country', 'Vehicle' => 'form_vehicle_running_cost', 'Entertainment' => 'form_entertainment_presentation', 'Meeting' => 'form_meeting_seminar', 'Others' => 'form_others'] as $label => $table) { + $data[$label] = DB::table($table)->where('cabang_id', $id)->whereIn('status', $statuses)->get(); + } + return view('backend.pages.dashboard.show', compact('cabang', 'data')); + } catch (\Exception $e) { + return redirect()->route('admin.dashboard')->with('error', 'Cabang tidak ditemukan.'); + } + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Backend/ReportController.php b/app/Http/Controllers/Backend/ReportController.php index 6fd791b..a8338ba 100644 --- a/app/Http/Controllers/Backend/ReportController.php +++ b/app/Http/Controllers/Backend/ReportController.php @@ -5,812 +5,403 @@ declare(strict_types=1); namespace App\Http\Controllers\Backend; use App\Http\Controllers\Controller; -use App\Models\AuditTrail; -use App\Models\FormUpCountry; -use App\Models\FormEntertaimentPresentation; -use App\Models\FormMeetingSeminar; -use App\Models\FormVehicleRunningCost; -use App\Models\FormOthers; -use App\Models\UserHasCabang; -use Illuminate\Support\Facades\DB; -use App\Models\Cabang; -use App\Models\Region; -use App\Helpers\FormHelper; -use App\Helpers\BudgetHelper; -use App\Models\Admin; +use App\Models\{ + Admin, Cabang, Region, Kategori, BudgetControlLog, + FormUpCountry, FormEntertaimentPresentation, FormMeetingSeminar, + FormVehicleRunningCost, FormOthers, UserHasCabang +}; +use App\Helpers\{FormHelper, BudgetHelper, PeriodeHelper}; +use Illuminate\Support\Facades\{DB, Schema}; use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\Schema; -use App\Models\Kategori; -use App\Models\BudgetControlLog; -use App\Helpers\PeriodeHelper; +use Illuminate\Http\Request; +use Illuminate\Database\Eloquent\Builder; class ReportController extends Controller -{ +{ + private function getAuthorizedUserIds(): ?array + { + $user = auth()->user(); + $role = $user->getRoleNames()[0] ?? null; + $userData = $user->getMyCabangAndRegionId(); + + return match ($role) { + 'Admin Region', 'Marketing Operational Manager Region' => + ($rid = $userData['region']) && $rid !== '-' + ? UserHasCabang::whereIn('cabang_id', Cabang::where('region_id', $rid)->pluck('id'))->pluck('user_id')->toArray() + : [], + + 'Area Manager Cabang' => + ($cid = $userData['cabang']) && $cid !== '-' + ? UserHasCabang::where('cabang_id', $cid)->pluck('user_id')->toArray() + : [], + + 'Medical Representatif' => [$user->id], + + default => null, + }; + } + + private function getFilteredForms(string $modelClass, array $withRelations = []) + { + $defaultRelations = ['user.region.cabang.region', 'user.cabang.cabang.region', 'rejector']; + $query = $modelClass::with(array_merge($defaultRelations, $withRelations)); + + $userIds = $this->getAuthorizedUserIds(); + if ($userIds !== null) { + $query->whereIn('user_id', $userIds); + } + + $this->applyStandardFilters($query, request()->all()); + return $query->orderBy('tanggal', 'desc')->get(); + } + + /** + * MODIFIKASI: Filter diubah dari rentang tanggal harian (start/end date) + * menjadi Rentang Periode Bulan Akuntansi (Bulan berjalan tgl 11 s/d Bulan Depan tgl 13) + */ + private function applyStandardFilters(Builder $query, array $params): void + { + if (!empty($params['region'])) { + $query->whereHas('user.region.cabang.region', fn($q) => $q->where('code', $params['region'])); + } + + if (!empty($params['cabang'])) { + $query->whereHas('user.cabang.cabang', fn($q) => $q->where('code', $params['cabang'])); + } + + if (!empty($params['status'])) { + $query->where('status', $params['status']); + } + + // --- PENYEMPURNAAN LOGIKA FILTER PERIODE BULAN CUT-OFF (11 s/d 13 Bulan Berikut) --- + if (!empty($params['month']) && !empty($params['year'])) { + $month = strtolower($params['month']); + $year = (int) $params['year']; + + [$startDate, $endDate] = PeriodeHelper::getPeriodeRange($month, $year); + + // Semua pencarian report kategori mengacu pada tanggal final approval/closing dalam rentang periode ini + $query->whereBetween('final_approved_at', [$startDate, $endDate]); + } + } + public function upcountry() { - $role = auth()->user()->getRoleNames()[0]; - $cabangs = collect(); // Default empty collection - $regions = collect(); // Default empty collection - $formsQuery = FormUpCountry::with([ - 'user.region', - 'user.cabang', - 'rayon' - ]); - - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - $region_id = auth()->user()->getMyCabangAndRegionId()['region']; - - if ($region_id) { - $cabangs = Cabang::where('region_id', $region_id)->get(); - $regions = Region::where('id', $region_id)->get(); - $userIds = UserHasCabang::whereIn('cabang_id', $cabangs->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } elseif ($role == 'Area Manager Cabang') { - $cabang_id = auth()->user()->getMyCabangAndRegionId()['cabang']; - - if ($cabang_id) { - $cabangs = Cabang::where('id', $cabang_id)->get(); - $regions = Region::where('id', $cabangs->first()->region_id)->get(); - $userIds = UserHasCabang::where('cabang_id', $cabang_id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } else { - $cabangs = Cabang::all(); - $regions = Region::all(); - } - - // Apply filters from URL params $params = request()->all(); - - if (isset($params['region'])) { - $region = Region::where('code', $params['region'])->first(); - if ($region) { - $userIds = UserHasCabang::whereIn('cabang_id', Cabang::where('region_id', $region->id)->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - if (isset($params['cabang'])) { - $cabang = Cabang::where('code', $params['cabang'])->first(); - if ($cabang) { - $userIds = UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - if (isset($params['start_date']) || isset($params['end_date'])) { - $startDate = $params['start_date'] ?? null; - $endDate = $params['end_date'] ?? null; - - if ($startDate && $endDate) { - $formsQuery->whereBetween('tanggal', [$startDate, $endDate]); - } elseif ($startDate) { - $formsQuery->where('tanggal', '>=', $startDate); - } elseif ($endDate) { - $formsQuery->where('tanggal', '<=', $endDate); - } - } - - if (isset($params['status'])) { - $formsQuery->where('status', $params['status']); - } - - // Fetch forms after applying all filters - $forms = $formsQuery->get(); - return view('backend.pages.report.upcountry', [ - 'pageInfo' => [ - 'title' => 'Generate Reports For Form Up Country', - ], - 'forms' => $forms, - 'cabang' => $cabangs, - 'regions' => $regions + 'pageInfo' => ['title' => 'Reports Form Up Country'], + 'forms' => $this->getFilteredForms(FormUpCountry::class, ['rayon']), + 'cabang' => Cabang::all(), + 'regions' => Region::all(), + 'year' => $params['year'] ?? Carbon::now()->year, + 'month' => $params['month'] ?? Carbon::now()->format('F'), ]); } - public function vehicle() { - $role = auth()->user()->getRoleNames()[0]; - $cabangs = collect(); // Default empty collection - $regions = collect(); // Default empty collection - $formsQuery = FormVehicleRunningCost::with([ - 'user.region', // Eager load 'region' within 'user' - 'user.cabang', // Eager load 'cabang' within 'user' - ]); - - // Apply role-based query logic - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - $region_id = auth()->user()->getMyCabangAndRegionId()['region']; - - if ($region_id) { - $cabangs = Cabang::where('region_id', $region_id)->get(); - $regions = Region::where('id', $region_id)->get(); - $userIds = UserHasCabang::whereIn('cabang_id', $cabangs->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } elseif ($role == 'Area Manager Cabang') { - $cabang_id = auth()->user()->getMyCabangAndRegionId()['cabang']; - - if ($cabang_id) { - $cabangs = Cabang::where('id', $cabang_id)->get(); - $regions = Region::where('id', $cabangs->first()->region_id)->get(); - $userIds = UserHasCabang::where('cabang_id', $cabang_id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } else { - // Default behavior for other roles (if no specific role is matched) - $cabangs = Cabang::all(); - $regions = Region::all(); - } - - // Get all params in URL for filtering $params = request()->all(); - - if ($params) { - // Filter by region - if (isset($params['region'])) { - $region = Region::where('code', $params['region'])->first(); - if ($region) { - $userIds = UserHasCabang::whereIn('cabang_id', Cabang::where('region_id', $region->id)->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Filter by cabang - if (isset($params['cabang'])) { - $cabang = Cabang::where('code', $params['cabang'])->first(); - if ($cabang) { - $userIds = UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Filter by date range - if (isset($params['start_date']) || isset($params['end_date'])) { - $startDate = $params['start_date'] ?? null; - $endDate = $params['end_date'] ?? null; - - if ($startDate && $endDate) { - $formsQuery->whereBetween('tanggal', [$startDate, $endDate]); - } elseif ($startDate) { - $formsQuery->where('tanggal', '>=', $startDate); - } elseif ($endDate) { - $formsQuery->where('tanggal', '<=', $endDate); - } - } - - // Filter by status - if (isset($params['status'])) { - $formsQuery->where('status', $params['status']); - } - } - - // Fetch forms after applying all filters - $forms = $formsQuery->get(); - return view('backend.pages.report.vehicle', [ - 'pageInfo' => [ - 'title' => 'Generate Reports For Form Vehicle Running Cost', - ], - 'forms' => $forms, - 'cabang' => $cabangs, - 'regions' => $regions + 'pageInfo' => ['title' => 'Reports Form Vehicle Running Cost'], + 'forms' => $this->getFilteredForms(FormVehicleRunningCost::class), + 'cabang' => Cabang::all(), + 'regions' => Region::all(), + 'year' => $params['year'] ?? Carbon::now()->year, + 'month' => $params['month'] ?? Carbon::now()->format('F'), ]); } public function entertainment() { - $role = auth()->user()->getRoleNames()[0]; - $cabangs = Cabang::all(); - $regions = Region::all(); - $formsQuery = FormEntertaimentPresentation::with([ - 'user.region', // Eager load 'region' within 'user' - 'user.cabang', // Eager load 'cabang' within 'user' - ]); - - // Apply role-based query logic - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - $region_id = auth()->user()->getMyCabangAndRegionId()['region']; - - if ($region_id) { - $cabangs = Cabang::where('region_id', $region_id)->get(); - $regions = Region::where('id', $region_id)->get(); - $userIds = UserHasCabang::whereIn('cabang_id', $cabangs->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } elseif ($role == 'Area Manager Cabang') { - $cabang_id = auth()->user()->getMyCabangAndRegionId()['cabang']; - - if ($cabang_id) { - $cabangs = Cabang::where('id', $cabang_id)->get(); - $regions = Region::where('id', $cabangs->first()->region_id)->get(); - $userIds = UserHasCabang::where('cabang_id', $cabang_id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Get all params in URL for filtering $params = request()->all(); - - if ($params) { - // Filter by region - if (isset($params['region'])) { - $region = Region::where('code', $params['region'])->first(); - if ($region) { - $userIds = UserHasCabang::whereIn('cabang_id', Cabang::where('region_id', $region->id)->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Filter by cabang - if (isset($params['cabang'])) { - $cabang = Cabang::where('code', $params['cabang'])->first(); - if ($cabang) { - $userIds = UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Filter by date range - if (isset($params['start_date']) || isset($params['end_date'])) { - $startDate = $params['start_date'] ?? null; - $endDate = $params['end_date'] ?? null; - - if ($startDate && $endDate) { - $formsQuery->whereBetween('tanggal', [$startDate, $endDate]); - } elseif ($startDate) { - $formsQuery->where('tanggal', '>=', $startDate); - } elseif ($endDate) { - $formsQuery->where('tanggal', '<=', $endDate); - } - } - - // Filter by status - if (isset($params['status'])) { - $formsQuery->where('status', $params['status']); - } - } - - // Fetch forms after applying all filters - $forms = $formsQuery->get(); - return view('backend.pages.report.entertainment', [ - 'pageInfo' => [ - 'title' => 'Generate Reports For Form Entertainment & Presentation', - ], - 'forms' => $forms, - 'cabang' => $cabangs, - 'regions' => $regions + 'pageInfo' => ['title' => 'Reports Form Entertainment & Presentation'], + 'forms' => $this->getFilteredForms(FormEntertaimentPresentation::class, ['attachments']), + 'cabang' => Cabang::all(), + 'regions' => Region::all(), + 'year' => $params['year'] ?? Carbon::now()->year, + 'month' => $params['month'] ?? Carbon::now()->format('F'), ]); } public function meeting() { - $role = auth()->user()->getRoleNames()[0]; - $cabangs = Cabang::all(); - $regions = Region::all(); - - // Initialize the query for FormMeetingSeminar with eager loading - $formsQuery = FormMeetingSeminar::with([ - 'user.region', // Eager load 'region' within 'user' - 'user.cabang', // Eager load 'cabang' within 'user' - ]); - - // Apply role-based query logic - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - $region_id = auth()->user()->getMyCabangAndRegionId()['region']; - - if ($region_id) { - $cabangs = Cabang::where('region_id', $region_id)->get(); - $regions = Region::where('id', $region_id)->get(); - $userIds = UserHasCabang::whereIn('cabang_id', $cabangs->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } elseif ($role == 'Area Manager Cabang') { - $cabang_id = auth()->user()->getMyCabangAndRegionId()['cabang']; - - if ($cabang_id) { - $cabangs = Cabang::where('id', $cabang_id)->get(); - $regions = Region::where('id', $cabangs->first()->region_id)->get(); - $userIds = UserHasCabang::where('cabang_id', $cabang_id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Get all params in URL for filtering $params = request()->all(); - - if ($params) { - // Filter by region - if (isset($params['region'])) { - $region = Region::where('code', $params['region'])->first(); - if ($region) { - $userIds = UserHasCabang::whereIn('cabang_id', Cabang::where('region_id', $region->id)->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Filter by cabang - if (isset($params['cabang'])) { - $cabang = Cabang::where('code', $params['cabang'])->first(); - if ($cabang) { - $userIds = UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Filter by date range - if (isset($params['start_date']) || isset($params['end_date'])) { - $startDate = $params['start_date'] ?? null; - $endDate = $params['end_date'] ?? null; - - if ($startDate && $endDate) { - $formsQuery->whereBetween('tanggal', [$startDate, $endDate]); - } elseif ($startDate) { - $formsQuery->where('tanggal', '>=', $startDate); - } elseif ($endDate) { - $formsQuery->where('tanggal', '<=', $endDate); - } - } - - // Filter by status - if (isset($params['status'])) { - $formsQuery->where('status', $params['status']); - } - } - - // Fetch forms after applying all filters - $forms = $formsQuery->get(); - return view('backend.pages.report.meeting', [ - 'pageInfo' => [ - 'title' => 'Generate Reports For Form Meeting & Seminar', - ], - 'forms' => $forms, - 'cabang' => $cabangs, - 'regions' => $regions + 'pageInfo' => ['title' => 'Reports Form Meeting & Seminar'], + 'forms' => $this->getFilteredForms(FormMeetingSeminar::class), + 'cabang' => Cabang::all(), + 'regions' => Region::all(), + 'year' => $params['year'] ?? Carbon::now()->year, + 'month' => $params['month'] ?? Carbon::now()->format('F'), ]); } public function others() { - $role = auth()->user()->getRoleNames()[0]; - $cabangs = Cabang::all(); - $regions = Region::all(); - - // Initialize the query for FormOthers with eager loading - $formsQuery = FormOthers::with([ - 'user.region', // Eager load 'region' within 'user' - 'user.cabang', // Eager load 'cabang' within 'user' - 'kategori' - ]); - - // Apply role-based query logic - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - $region_id = auth()->user()->getMyCabangAndRegionId()['region']; - - if ($region_id) { - $cabangs = Cabang::where('region_id', $region_id)->get(); - $regions = Region::where('id', $region_id)->get(); - $userIds = UserHasCabang::whereIn('cabang_id', $cabangs->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } elseif ($role == 'Area Manager Cabang') { - $cabang_id = auth()->user()->getMyCabangAndRegionId()['cabang']; - - if ($cabang_id) { - $cabangs = Cabang::where('id', $cabang_id)->get(); - $regions = Region::where('id', $cabangs->first()->region_id)->get(); - $userIds = UserHasCabang::where('cabang_id', $cabang_id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Get all params in URL for filtering $params = request()->all(); - - if ($params) { - // Filter by region - if (isset($params['region'])) { - $region = Region::where('code', $params['region'])->first(); - if ($region) { - $userIds = UserHasCabang::whereIn('cabang_id', Cabang::where('region_id', $region->id)->pluck('id'))->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Filter by cabang - if (isset($params['cabang'])) { - $cabang = Cabang::where('code', $params['cabang'])->first(); - if ($cabang) { - $userIds = UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id'); - $formsQuery->whereIn('user_id', $userIds); - } - } - - // Filter by date range - if (isset($params['start_date']) || isset($params['end_date'])) { - $startDate = $params['start_date'] ?? null; - $endDate = $params['end_date'] ?? null; - - if ($startDate && $endDate) { - $formsQuery->whereBetween('tanggal', [$startDate, $endDate]); - } elseif ($startDate) { - $formsQuery->where('tanggal', '>=', $startDate); - } elseif ($endDate) { - $formsQuery->where('tanggal', '<=', $endDate); - } - } - - // Filter by status - if (isset($params['status'])) { - $formsQuery->where('status', $params['status']); - } - } - - // Fetch forms after applying all filters - $forms = $formsQuery->get(); - return view('backend.pages.report.others', [ - 'pageInfo' => [ - 'title' => 'Generate Reports For Form Others', - ], - 'forms' => $forms, - 'cabang' => $cabangs, - 'regions' => $regions + 'pageInfo' => ['title' => 'Reports Form Others'], + 'forms' => $this->getFilteredForms(FormOthers::class, ['kategori']), + 'cabang' => Cabang::all(), + 'regions' => Region::all(), + 'year' => $params['year'] ?? Carbon::now()->year, + 'month' => $params['month'] ?? Carbon::now()->format('F'), ]); } public function all() { - $role = auth()->user()->getRoleNames()[0]; - $cabangs = collect(); - $regions = collect(); + $params = request()->all(); + $userFilter = $this->getAuthorizedUserIds(); $availableBudget = 0; - $currentMonthKey = strtolower(date('F')); - $currentYear = (int) date('Y'); - - $userRegionData = auth()->user()->getMyCabangAndRegionId(); - $region_id = isset($userRegionData['region']) && $userRegionData['region'] !== '-' ? (int) $userRegionData['region'] : null; - $cabang_id = isset($userRegionData['cabang']) && $userRegionData['cabang'] !== '-' ? (int) $userRegionData['cabang'] : null; - $userId = auth()->user()->id; - - $userFilter = null; - $statusFilter = null; - $dateRange = null; - $selectedMonth = $currentMonthKey; - $selectedYear = $currentYear; - - $monthLookup = [ - 'january' => 'January', - 'february' => 'February', - 'march' => 'March', - 'april' => 'April', - 'may' => 'May', - 'june' => 'June', - 'july' => 'July', - 'august' => 'August', - 'september' => 'September', - 'october' => 'October', - 'november' => 'November', - 'december' => 'December', - ]; - - $applyUserFilter = static function (array $ids) use (&$userFilter): void { - $ids = array_values(array_unique(array_filter($ids, static fn ($id) => !is_null($id)))); - - if (empty($ids)) { - $userFilter = []; - return; - } - - if (is_null($userFilter)) { - $userFilter = $ids; - } else { - $userFilter = array_values(array_intersect($userFilter, $ids)); - } - }; - - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - if ($region_id) { - $cabangs = Cabang::where('region_id', $region_id)->get(); - $regions = Region::where('id', $region_id)->get(); - $userIds = UserHasCabang::whereIn('cabang_id', $cabangs->pluck('id'))->pluck('user_id')->toArray(); - $applyUserFilter($userIds); - } else { - $userFilter = []; - } - } elseif ($role == 'Area Manager Cabang') { - if ($cabang_id) { - $cabangs = Cabang::where('id', $cabang_id)->get(); - $regions = $cabangs->isNotEmpty() ? Region::where('id', $cabangs->first()->region_id)->get() : collect(); - $userIds = UserHasCabang::where('cabang_id', $cabang_id)->pluck('user_id')->toArray(); - $applyUserFilter($userIds); - } else { - $userFilter = []; - } - } elseif ($role == 'Medical Representatif') { - $applyUserFilter([$userId]); - $cabangs = collect(); - $regions = collect(); - } else { - $cabangs = Cabang::all(); - $regions = Region::all(); - } - - $params = request()->all(); - - if (!empty($params['region'])) { - $selectedRegion = Region::where('code', $params['region'])->first(); - $regionUserIds = $selectedRegion - ? UserHasCabang::whereIn('cabang_id', Cabang::where('region_id', $selectedRegion->id)->pluck('id'))->pluck('user_id')->toArray() - : []; - $applyUserFilter($regionUserIds); - - if ($selectedRegion && ($regions->isEmpty() || in_array($role, ['Superadmin', 'Admin'], true))) { - $regions = Region::all(); - } - } - - if (!empty($params['month']) || !empty($params['year'])) { - $selectedMonth = strtolower($params['month'] ?? $selectedMonth); - $selectedYear = (int) ($params['year'] ?? $selectedYear); - - if (!isset($monthLookup[$selectedMonth])) { - $selectedMonth = $currentMonthKey; - } - - $monthName = $monthLookup[$selectedMonth]; - $startOfMonth = Carbon::parse("first day of {$monthName} {$selectedYear}")->startOfDay(); - $endOfMonth = Carbon::parse("last day of {$monthName} {$selectedYear}")->endOfDay(); - $dateRange = [$startOfMonth, $endOfMonth]; - } + $selectedMonth = strtolower($params['month'] ?? Carbon::now()->format('F')); + $selectedYear = (int) ($params['year'] ?? Carbon::now()->year); if (!empty($params['cabang'])) { - $selectedCabang = Cabang::where('code', $params['cabang'])->first(); - $cabangUserIds = $selectedCabang - ? UserHasCabang::where('cabang_id', $selectedCabang->id)->pluck('user_id')->toArray() - : []; - $applyUserFilter($cabangUserIds); - - if ($selectedCabang) { - $availableBudget = BudgetHelper::getAvailableBudget($selectedCabang->id, $selectedMonth, $selectedYear); - } - } elseif ($cabang_id && $dateRange) { - $cabang = Cabang::find($cabang_id); + $cabang = Cabang::where('code', $params['cabang'])->first(); if ($cabang) { $availableBudget = BudgetHelper::getAvailableBudget($cabang->id, $selectedMonth, $selectedYear); + $uIds = UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id')->toArray(); + $userFilter = $userFilter === null ? $uIds : array_values(array_intersect($userFilter, $uIds)); } } - if (!empty($params['status'])) { - $statusFilter = $params['status']; - } + // Sinkronisasi logika rentang tanggal 11 s/d 13 bulan depan untuk database konsolidasi (All Forms) + [$start, $end] = PeriodeHelper::getPeriodeRange($selectedMonth, $selectedYear); $tables = [ - 'form_others', - 'form_up_country', - 'form_vehicle_running_cost', - 'form_meeting_seminar', - 'form_entertainment_presentation', + 'form_others' => 'bukti_total', + 'form_up_country' => 'NULL', + 'form_vehicle_running_cost' => 'bukti_total', + 'form_meeting_seminar' => 'NULL', + 'form_entertainment_presentation' => 'bukti_total' ]; $formsQuery = null; - foreach ($tables as $table) { - $dateColumn = Schema::hasColumn($table, 'tanggal') ? 'tanggal' : 'created_at'; + foreach ($tables as $table => $mainProofCol) { + $query = DB::table($table)->selectRaw(" + id, + expense_number, + '$table' as type, + user_id, + total, + approved_total, + account_number, + status, + final_approved_at as report_date, + created_at, + $mainProofCol as main_proof, + rejected_by, + rejected_at, + remarks + ") + ->when($userFilter, fn($q) => $q->whereIn('user_id', $userFilter)) + ->when(!empty($params['status']), fn($q) => $q->where('status', $params['status'])) + // Kunci dengan rentang tanggal closing resmi 11 s/d 13 bulan berikutnya + ->whereBetween('final_approved_at', [$start, $end]) + ->when(Schema::hasColumn($table, 'deleted_at'), fn($q) => $q->whereNull('deleted_at')); - $query = DB::table($table) - ->selectRaw(" - id, - expense_number, - '$table' as type, - user_id, - total, - approved_total, - account_number, - status, - $dateColumn as report_date, - created_at - "); - - if (!is_null($userFilter)) { - $query->whereIn('user_id', $userFilter); - } - - if ($statusFilter !== null) { - $query->where('status', $statusFilter); - } - - if ($dateRange) { - $query->whereBetween($dateColumn, $dateRange); - } - - if (Schema::hasColumn($table, 'deleted_at')) { - $query->whereNull('deleted_at'); - } - - $formsQuery = $formsQuery - ? $formsQuery->unionAll($query) - : $query; + $formsQuery = $formsQuery ? $formsQuery->unionAll($query) : $query; } - if (!$formsQuery) { - $forms = collect(); - } else { - $forms = DB::query() - ->fromSub($formsQuery, 'forms') - ->join('admins', 'admins.id', '=', 'forms.user_id') - ->select('forms.*', 'admins.id as admin_id') - ->orderByDesc('forms.report_date') - ->paginate(100) - ->appends($params); + $forms = collect(); + if ($formsQuery) { + $forms = DB::query()->fromSub($formsQuery, 'forms') + ->orderByDesc('report_date') + ->paginate(100)->appends($params); - $forms->getCollection()->transform(function ($item) { - $item->user = \App\Models\Admin::with([ - 'region.cabang.region', - 'cabang.cabang.region', - 'rayon.region', - ])->find($item->admin_id); - $item->type = ucwords(str_replace('_', ' ', str_replace('form_', '', $item->type))); + $items = collect($forms->items()); + $formIds = $items->pluck('id'); + + $submitterIds = $items->pluck('user_id')->filter()->unique(); + $rejectorIds = $items->pluck('rejected_by')->filter()->unique(); + $allAdminIds = $submitterIds->merge($rejectorIds)->unique(); + $admins = Admin::with(['region.cabang.region', 'cabang.cabang.region']) + ->whereIn('id', $allAdminIds) + ->get() + ->keyBy('id'); + + $attachments = \App\Models\AttachmentForm::whereIn('form_id', $formIds) + ->get() + ->groupBy(fn($q) => $q->table_name . '_' . $q->form_id); + + $forms->getCollection()->transform(function ($item) use ($admins, $attachments) { + $item->user = $admins->get($item->user_id); + $item->rejector = $admins->get($item->rejected_by); + $item->type_label = ucwords(str_replace(['form_', '_'], ['', ' '], (string)$item->type)); + + $key = $item->type . '_' . $item->id; + $item->extra_attachments = $attachments->get($key) ?? collect(); + return $item; }); } return view('backend.pages.report.all', [ - 'pageInfo' => [ - 'title' => 'Generate Reports For All Forms', - ], + 'pageInfo' => ['title' => 'Reports For All Forms'], 'forms' => $forms, - 'cabangs' => $cabangs, - 'regions' => $regions, + 'cabangs' => Cabang::all(), + 'regions' => Region::all(), 'availableBudget' => $availableBudget, + 'year' => $selectedYear, + 'month' => $selectedMonth, ]); } public function jurnal() { - $role = auth()->user()->getRoleNames()[0]; - $cabangs = []; - $regions = []; + $user = auth()->user(); + $role = $user->getRoleNames()[0] ?? null; + $userData = $user->getMyCabangAndRegionId(); + + $regions = Region::all(); + $cabangs = Cabang::all(); - if ($role == 'Admin Region' || $role == 'Marketing Operational Manager Region') { - $region_id = auth()->user()->getMyCabangAndRegionId()['region']; - - if ($region_id) { - $cabangs = Cabang::where('region_id', $region_id)->get(); - $regions = Region::where('id', $region_id)->get(); - } - } else if ($role == 'Area Manager Cabang') { - $cabang_id = auth()->user()->getMyCabangAndRegionId()['cabang']; - - if ($cabang_id) { - $cabangs = Cabang::where('id', $cabang_id)->get(); - $regions = Region::where('id', $cabangs[0]->region_id)->get(); - } - } else if ($role == 'Superadmin') { - $regions = Region::all(); - $cabangs = collect(); - $selectedRegionCode = request()->get('region'); - - if ($selectedRegionCode) { - $selectedRegion = Region::where('code', $selectedRegionCode)->first(); - - if ($selectedRegion) { - $cabangs = Cabang::where('region_id', $selectedRegion->id)->get(); - } else { - $cabangs = Cabang::all(); - } - } else { - $cabangs = Cabang::all(); - } - } else { - $cabangs = Cabang::all(); - $regions = Region::all(); + if (in_array($role, ['Admin Region', 'Marketing Operational Manager Region'])) { + $regions = Region::where('id', $userData['region'])->get(); + $cabangs = Cabang::where('region_id', $userData['region'])->get(); + } elseif ($role == 'Area Manager Cabang') { + $cabangs = Cabang::where('id', $userData['cabang'])->get(); + $regions = Region::where('id', $cabangs->first()->region_id ?? 0)->get(); } return view('backend.pages.report.jurnal', [ - 'pageInfo' => [ - 'title' => 'Generate Reports For Jurnal', - ], + 'pageInfo' => ['title' => 'Generate Reports For Jurnal'], 'cabang' => $cabangs, 'regions' => $regions ]); } - - public function generateJurnal() + public function generateJurnal(Request $request) { - $params = request()->all(); + $request->validate([ + 'cabang' => 'required', + 'year' => 'required', + 'month' => 'required', + ]); - $kategori_bca = Kategori::where('name', 'Cash in Bank BCA')->first(); - $excludedCategories = [ - 'Cash in Bank BCA', - 'Donasi/Gift', - 'PPn Masukan', - 'Vehicle Maintenance', - 'Bahan Promosi', - 'Sponsorship', - 'Presentation', - ]; + $params = $request->all(); + $accounting_period = $params['accounting_period'] ?? '-'; - $kategoris = Kategori::whereNotIn('name', $excludedCategories)->get(); - $cabang = Cabang::with('cost')->where('code', $params['cabang'])->firstOrFail(); + $excluded = ['Cash in Bank BCA', 'Donasi/Gift', 'PPn Masukan', 'Vehicle Maintenance', 'Bahan Promosi', 'Sponsorship']; + $kategoris = \App\Models\Kategori::whereNotIn('name', $excluded)->get(); + $kategori_bca = \App\Models\Kategori::where('name', 'Cash in Bank BCA')->first(); + + $cabang = \App\Models\Cabang::where('code', $params['cabang'])->firstOrFail(); + $year = (int) $params['year']; + $month = strtolower($params['month']); - $year = $params['year']; - $month = $params['month']; // format: angka atau teks bulan (pastikan valid) - - // Hitung range periode (11 bulan_ini - 10 bulan_depan) + // Memanggil rentang baru 11 s/d 13 bulan depan untuk pembuatan Jurnal Akurat [$startDate, $endDate] = PeriodeHelper::getPeriodeRange($month, $year); - // Ambil data terakhir dari log budget - $budget = BudgetControlLog::where('cabang_id', $cabang->id) + $pettyCashAmount = \App\Models\BudgetControl::where('cabang_id', $cabang->id) ->where('periode_year', $year) ->where('periode_month', $month) - ->orderBy('created_at', 'desc') - ->first(); + ->sum('amount'); - $userIds = UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id'); + $budget = \App\Models\BudgetControlLog::where('cabang_id', $cabang->id) + ->where('periode_year', $year) + ->where('periode_month', $month) + ->orderByDesc('created_at')->first(); - // Fetch semua forms berdasarkan periode range - $forms = [ - 'Up Country' => FormUpCountry::whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $endDate]) - ->whereIn('status', ['Approved', 'Closed']) - ->get(), + $userIds = \App\Models\UserHasCabang::where('cabang_id', $cabang->id)->pluck('user_id'); - 'Vehicle Running Cost' => FormVehicleRunningCost::whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $endDate]) - ->whereIn('status', ['Approved', 'Closed']) - ->get(), - - 'Entertainment' => FormEntertaimentPresentation::whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $endDate]) - ->whereIn('status', ['Approved', 'Closed']) - ->get(), - - 'Meeting / Seminar' => FormMeetingSeminar::whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $endDate]) - ->whereIn('status', ['Approved', 'Closed']) - ->get(), + $formModels = [ + 'up country' => \App\Models\FormUpCountry::class, + 'vehicle running cost' => \App\Models\FormVehicleRunningCost::class, + 'entertainment' => \App\Models\FormEntertaimentPresentation::class, + 'presentation' => \App\Models\FormEntertaimentPresentation::class, + 'sponsorship' => \App\Models\FormEntertaimentPresentation::class, + 'meeting / seminar' => \App\Models\FormMeetingSeminar::class, ]; - // Forms "Others" dikelompokkan berdasarkan kategori - $otherForms = FormOthers::whereIn('user_id', $userIds) - ->whereBetween('final_approved_at', [$startDate, $endDate]) - ->whereIn('status', ['Approved', 'Closed']) - ->get(); - - // Total nominal per kategori $nominals = []; - foreach ($kategoris as $item) { - if (isset($forms[$item->name])) { - $nominals[$item->id] = $forms[$item->name]->sum('approved_total'); + foreach ($kategoris as $kategori) { + $katKey = strtolower(trim($kategori->name)); + $sum = 0; + + if (isset($formModels[$katKey])) { + $model = $formModels[$katKey]; + + $query = $model::whereIn('user_id', $userIds) + ->whereBetween('final_approved_at', [$startDate, $endDate]) + ->where('status', 'Closed'); + + if ($model === \App\Models\FormEntertaimentPresentation::class) { + $query->where('kategori_id', $kategori->id) + ->where(function($q) use ($katKey) { + $q->where('jenis', 'LIKE', '%' . $katKey . '%'); + }); + } else { + if (\Illuminate\Support\Facades\Schema::hasColumn((new $model)->getTable(), 'kategori_id')) { + $query->where('kategori_id', $kategori->id); + } + } + + $sum = $query->sum('approved_total'); } else { - $nominals[$item->id] = $otherForms->where('kategori_id', $item->id)->sum('approved_total'); + $sum = \App\Models\FormOthers::whereIn('user_id', $userIds) + ->where('kategori_id', $kategori->id) + ->whereBetween('final_approved_at', [$startDate, $endDate]) + ->where('status', 'Closed') + ->sum('approved_total'); } + $nominals[$kategori->id] = $sum; } - $totalKategori = array_sum($nominals); - $pettyCashAmount = $budget && $budget->amount ? $budget->amount : 0; + $totalDebet = array_sum($nominals); return view('backend.pages.report.generate_jurnal', [ - 'pageInfo' => [ - 'title' => 'Generate Reports For Jurnal', - ], - 'kategori' => $kategoris, - 'cabang' => $cabang, - 'year' => $year, - 'month' => $month, - 'kategori_bca' => $kategori_bca, - 'nominals' => $nominals, - 'budget' => $budget, - 'totalKategori' => $totalKategori, - 'pettyCashAmount' => $pettyCashAmount, + 'pageInfo' => ['title' => 'Generate Reports For Jurnal'], + 'kategori' => $kategoris, + 'cabang' => $cabang, + 'year' => $year, + 'month' => $month, + 'kategori_bca' => $kategori_bca, + 'nominals' => $nominals, + 'budget' => $budget, + 'totalKategori' => $totalDebet, + 'pettyCashAmount' => $pettyCashAmount, + 'cashInBca' => $pettyCashAmount - $totalDebet, + 'accounting_period' => $accounting_period, ]); } -} + + public function reportRejected(Request $request) + { + $models = [ + \App\Models\FormUpCountry::class, + \App\Models\FormEntertaimentPresentation::class, + \App\Models\FormVehicleRunningCost::class, + \App\Models\FormMeetingSeminar::class, + \App\Models\FormOthers::class + ]; + + $allRejected = collect(); + + foreach ($models as $model) { + $data = $model::with(['user', 'cabang', 'rejector']) + ->where('status', 'Rejected') + ->when($request->start_date, function ($query) use ($request) { + return $query->whereBetween('rejected_at', [$request->start_date, $request->end_date]); + }) + ->get(); + + $allRejected = $allRejected->concat($data); + } + + $allRejected = $allRejected->sortByDesc('rejected_at'); + + return view('backend.report.report_rejected', [ + 'data' => $allRejected, + 'start_date' => $request->start_date, + 'end_date' => $request->end_date, + 'year' => $request->year ?? Carbon::now()->year, + 'month' => $request->month ?? Carbon::now()->format('F'), + ]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Forms/FormEntertainmentPresentationController.php b/app/Http/Controllers/Forms/FormEntertainmentPresentationController.php index 18fe53f..0625631 100644 --- a/app/Http/Controllers/Forms/FormEntertainmentPresentationController.php +++ b/app/Http/Controllers/Forms/FormEntertainmentPresentationController.php @@ -76,7 +76,7 @@ class FormEntertainmentPresentationController extends Controller $role = auth()->user()->getRoleNames()[0]; $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $closingDay = (int) env('CLOSING_DATE', 13); $userRegionData = auth()->user()->getMyCabangAndRegionId(); $regionContextId = $userRegionData['region'] !== '-' ? $userRegionData['region'] : null; $cabangContextId = $userRegionData['cabang'] !== '-' ? $userRegionData['cabang'] : null; @@ -182,15 +182,15 @@ class FormEntertainmentPresentationController extends Controller ]); } - public function create() - { + public function create() + { $this->checkAuthorization(auth()->user(), ['forms.entertainment.create']); - return view('backend.pages.forms.entertainment.create'); - } + return view('backend.pages.forms.entertainment.create'); + } - public function store(Request $request) - { + public function store(Request $request) + { $this->checkAuthorization(auth()->user(), ['forms.entertainment.create']); $validator = Validator::make($request->all(), [ @@ -201,6 +201,9 @@ class FormEntertainmentPresentationController extends Controller 'name' => 'required|string', 'alamat' => 'required|string', 'nik_or_npwp' => 'required|string', + 'jabatan' => 'required|string', + 'nama_perusahaan' => 'required|string', + 'jenis_usaha' => 'required|string', 'attachments' => 'nullable|array', 'attachments.*.file_category' => 'nullable|string|in:' . implode(',', array_keys($this->attachmentCategories)), 'attachments.*.file_path' => 'nullable|file|max:10240', @@ -209,24 +212,26 @@ class FormEntertainmentPresentationController extends Controller $validator->validate(); $tanggal = Carbon::parse($request->tanggal); + + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); // Ambil tanggal sekarang untuk tentukan periode berjalan $now = Carbon::now(); if ($now->day >= $startDay) { - // Periode berjalan dari bulan ini sampai bulan depan - $startDate = Carbon::create($now->year, $now->month, $startDay); - $closingDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $closingDay); + // Jendela Aktif: Tgl 11 Bulan Ini s/d Tgl 10 Bulan Depan + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); } else { - // Periode berjalan dari bulan lalu sampai bulan ini - $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay); - $closingDate = Carbon::create($now->year, $now->month, $closingDay); + // Jendela Aktif: Tgl 11 Bulan Lalu s/d Tgl 10 Bulan Ini + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); } - if ($tanggal->lt($startDate) || $tanggal->gt($closingDate)) { - session()->flash('error', "Gagal, tanggal tidak berada dalam periode input: {$startDate->format('d M')} - {$closingDate->format('d M')}"); + if ($tanggal->lt($startDate) || $tanggal->gt($maxInputDate)) { + session()->flash('error', "Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: {$startDate->format('d M Y')} - {$maxInputDate->format('d M Y')}"); return redirect()->back()->withInput(); } @@ -244,19 +249,19 @@ class FormEntertainmentPresentationController extends Controller $region = Region::where('id', $cabang->region_id)->first(); - $kategori = ""; + $kategori = null; if (strtolower($request->jenis) == 'entertainment' || $request->jenis == 'entertainment') { - $kategori = Kategori::where('name', 'Entertainment')->first(); + $kategori = Kategori::where('name', 'LIKE', '%Entertainment%')->first(); } elseif (strtolower($request->jenis) == 'presentation' || $request->jenis == 'presentation') { - $kategori = Kategori::where('name', 'Presentation')->first(); + $kategori = Kategori::where('name', 'LIKE', '%Presentation%')->first(); } elseif (strtolower($request->jenis) == 'sponsorship' || $request->jenis == 'sponsorship') { - $kategori = Kategori::where('name', 'Sponsorship')->first(); + $kategori = Kategori::where('name', 'LIKE', '%Sponsorship%')->first(); } $folderPath = '/expense/' . $region->code . '/' . $cabang->code . '/entertainment'; // Generate sequence number and expense number - $sequence_number = FormEntertaimentPresentation::withTrashed()->where('expense_number', 'like', $region->code . '-' . $cabang->code . '-ETY-' . date('ym') . '%')->count() + 1; + $sequence_number = FormEntertaimentPresentation::withTrashed()->where('expense_number', 'like', $region->code . '-' . $cabang->code . '-ETY-' . date('ym') . '%')->count() + 1; $formatted_sequence_number = str_pad($sequence_number, 4, '0', STR_PAD_LEFT); $expense_number = $region->code . '-' . $cabang->code . '-ETY-' . date('ym') . $formatted_sequence_number; @@ -275,6 +280,7 @@ class FormEntertainmentPresentationController extends Controller $form = FormEntertaimentPresentation::create([ 'expense_number' => $expense_number, 'user_id' => auth()->user()->id, + 'cabang_id' => $cabang->id, // <--- REPARASI SUNTIK CABANG_ID BARU 'tanggal' => $request->tanggal, 'jenis' => $request->jenis, 'keterangan' => $request->keterangan, @@ -282,8 +288,12 @@ class FormEntertainmentPresentationController extends Controller 'name' => $request->name, 'alamat' => $request->alamat, 'nik_or_npwp' => $request->nik_or_npwp, + 'jabatan' => $request->jabatan, + 'nama_perusahaan' => $request->nama_perusahaan, + 'jenis_usaha' => $request->jenis_usaha, 'bukti_total' => $primaryAttachmentPath, - 'account_number' => $kategori->account_number, + 'account_number' => $kategori->account_number ?? null, + 'kategori_id' => $kategori->id ?? null, 'status' => 'On Progress', ]); @@ -388,10 +398,10 @@ class FormEntertainmentPresentationController extends Controller AuditTrailHelper::AddAuditTrail('Insert', 'Insert New Expense at Form Entertainment Presentation (' . $expense_number . ')'); session()->flash('success', 'Form has been created.'); return redirect()->back(); - } + } - public function edit($id) - { + public function edit($id) + { $this->checkAuthorization(auth()->user(), ['forms.entertainment.edit']); $role = auth()->user()->getRoleNames()[0]; @@ -403,39 +413,49 @@ class FormEntertainmentPresentationController extends Controller $attachments = $this->formatAttachmentCollection($form); - return view('backend.pages.forms.entertainment.edit', [ + return view('backend.pages.forms.entertainment.edit', [ 'form' => $form, 'attachments' => $attachments, - ]); - } + ]); + } - public function update(Request $request, $id) - { + public function update(Request $request, $id) + { $this->checkAuthorization(auth()->user(), ['forms.entertainment.edit']); $role = auth()->user()->getRoleNames()[0]; $form = FormEntertaimentPresentation::with('attachments')->findOrFail($id); - $cabang_id = UserHasCabang::where('user_id', $form->user_id)->first()?->cabang_id; + $cabang = UserHasCabang::with('cabang')->where('user_id', $form->user_id)->first()?->cabang; - if (!$cabang_id) { + if (!$cabang) { return redirect()->back()->with('error', 'Cabang tidak ditemukan.'); } - // Hitung periode berdasarkan tanggal form $tanggal = Carbon::parse($request->tanggal); + + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE (Konsisten dengan fungsi Store) $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); + + $now = Carbon::now(); - // Periode aktif berdasarkan tanggal input - if ($tanggal->day >= $startDay) { - $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); + if ($now->day >= $startDay) { + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); } else { - $periodeDate = Carbon::create($tanggal->copy()->subMonth()->year, $tanggal->copy()->subMonth()->month, $startDay); + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); } - $periodeMonth = strtolower($periodeDate->format('F')); - $periodeYear = (int) $periodeDate->format('Y'); + if ($tanggal->lt($startDate) || $tanggal->gt($maxInputDate)) { + session()->flash('error', "Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: {$startDate->format('d M Y')} - {$maxInputDate->format('d M Y')}"); + return redirect()->back()->withInput(); + } - $availableBudget = BudgetHelper::getAvailableBudget($cabang_id, $periodeMonth, $periodeYear); + // Hitung periode berdasarkan tanggal form untuk validasi budget + $periodeMonth = strtolower($startDate->format('F')); + $periodeYear = (int) $startDate->format('Y'); + + $availableBudget = BudgetHelper::getAvailableBudget($cabang->id, $periodeMonth, $periodeYear); $validator = Validator::make($request->all(), [ 'tanggal' => 'required|date', @@ -445,6 +465,9 @@ class FormEntertainmentPresentationController extends Controller 'name' => 'required|string', 'alamat' => 'required|string', 'nik_or_npwp' => 'required|string', + 'jabatan' => 'required|string', + 'nama_perusahaan' => 'required|string', + 'jenis_usaha' => 'required|string', 'attachments' => 'nullable|array', 'attachments.*.file_category' => 'nullable|string|in:' . implode(',', array_keys($this->attachmentCategories)), 'attachments.*.file_path' => 'nullable|file|max:10240', @@ -457,16 +480,15 @@ class FormEntertainmentPresentationController extends Controller return redirect()->back(); } - $cabang = UserHasCabang::with('cabang')->where('user_id', $form->user_id)->first()->cabang; $region = Region::where('id', $cabang->region_id)->first(); - $kategori = ""; + $kategori = null; if (strtolower($request->jenis) == 'entertainment' || $request->jenis == 'entertainment') { - $kategori = Kategori::where('name', 'Entertainment')->first(); + $kategori = Kategori::where('name', 'LIKE', '%Entertainment%')->first(); } elseif (strtolower($request->jenis) == 'presentation' || $request->jenis == 'presentation') { - $kategori = Kategori::where('name', 'Presentation')->first(); + $kategori = Kategori::where('name', 'LIKE', '%Presentation%')->first(); } elseif (strtolower($request->jenis) == 'sponsorship' || $request->jenis == 'sponsorship') { - $kategori = Kategori::where('name', 'Sponsorship')->first(); + $kategori = Kategori::where('name', 'LIKE', '%Sponsorship%')->first(); } $folderPath = '/expense/' . $region->code . '/' . $cabang->code . '/entertainment'; @@ -479,6 +501,7 @@ class FormEntertainmentPresentationController extends Controller : $form->bukti_total; $form->update([ + 'cabang_id' => $cabang->id, // <--- REPARASI SUNTIK CABANG_ID TERBARU 'tanggal' => $request->tanggal, 'jenis' => $request->jenis, 'keterangan' => $request->keterangan, @@ -486,8 +509,12 @@ class FormEntertainmentPresentationController extends Controller 'name' => $request->name, 'alamat' => $request->alamat, 'nik_or_npwp' => $request->nik_or_npwp, + 'jabatan' => $request->jabatan, + 'nama_perusahaan' => $request->nama_perusahaan, + 'jenis_usaha' => $request->jenis_usaha, 'bukti_total' => $primaryAttachmentPath, - 'account_number' => $kategori->account_number, + 'account_number' => $kategori->account_number ?? $form->account_number, + 'kategori_id' => $kategori->id ?? null, ]); if (!empty($attachmentsPayload)) { @@ -518,7 +545,7 @@ class FormEntertainmentPresentationController extends Controller AuditTrailHelper::AddAuditTrail('Update', 'Update Expense at Form Entertainment Presentation (' . $form->expense_number . ')'); session()->flash('success', 'Form has been updated.'); return redirect()->back(); - } + } public function deleteAttachment(Request $request, $formId, $attachmentId) { @@ -731,7 +758,8 @@ class FormEntertainmentPresentationController extends Controller return redirect()->back(); } - public function finalApprove($id) + // <--- REPARASI SUNTIKAN REQUEST DARI JAVASCRIPT MODAL AGAR DATA BISA DIAMBIL + public function finalApprove(Request $request, $id) { $this->checkAuthorization(auth()->user(), ['final_approval.approve']); $form = FormEntertaimentPresentation::findOrFail($id); @@ -748,11 +776,10 @@ class FormEntertainmentPresentationController extends Controller return redirect()->back(); } - // Hitung periode dari tanggal form + // Hitung periode berdasarkan tanggal form $tanggal = Carbon::parse($form->tanggal); $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); - + if ($tanggal->day >= $startDay) { $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); } else { @@ -762,13 +789,17 @@ class FormEntertainmentPresentationController extends Controller $periodeMonth = strtolower($periodeDate->format('F')); $periodeYear = (int) $periodeDate->format('Y'); + // <--- REPARASI: Penangkapan nilai payload dari checkbox JavaScript + $approvedTotal = $request->input('approved_total', $form->total); + $availableBudget = BudgetHelper::getAvailableBudget($cabang_id, $periodeMonth, $periodeYear); - if($form->approved_total > $availableBudget) { + if($approvedTotal > $availableBudget) { // <--- DISINKRONKAN DENGAN NILAI BARU HASIL CHECKLIST session()->flash('error', 'Budget cabang untuk periode ini tidak mencukupi, silahkan ajukan pada periode expense berikutnya'); return redirect()->back(); } $form->update([ + 'approved_total' => $approvedTotal, // <--- SUNTIK DATA UPDATE HASIL CHECKLIST KE DATABASE 'final_approved_at' => now(), 'final_approved_by' => auth()->user()->id, 'status' => 'Closed', @@ -777,7 +808,7 @@ class FormEntertainmentPresentationController extends Controller $name = $form->user->name; $expense_number = $form->expense_number; $tanggal = $form->created_at; - $total = $form->approved_total; + $total = $form->approved_total; // <--- TOTAL BARU YANG DIKIRIMKAN KE EMAIL / WHATSAPP $roles = ['Head of Sales Marketing', 'Marketing Operational Manager Region']; $recipients = [$form->user->email, auth()->user()->email]; @@ -850,75 +881,77 @@ class FormEntertainmentPresentationController extends Controller public function reject(Request $request, $id) { - $this->checkAuthorization(auth()->user(), ['approval.reject']); + // Otorisasi khusus MOM Region dan Atasan + if (!auth()->user()->hasAnyPermission(['approval.reject', 'approval2.reject', 'final_approval.approve'])) { + abort(403, 'Akses Ditolak: Anda tidak memiliki izin untuk melakukan Reject.'); + } $request->validate([ - 'remarks' => 'required|string|max:500', + 'remarks' => 'required|string|max:500', ]); + // Menggunakan Model Entertainment & Presentation $form = FormEntertaimentPresentation::findOrfail($id); + $form->update([ - 'status' => 'Rejected', - 'remarks' => $request->remarks, + 'status' => 'Rejected', + 'remarks' => $request->remarks, + 'rejected_by' => auth()->id(), // Kolom baru + 'rejected_at' => now(), // Kolom baru ]); $heads = $form->user->getCabangAndRegionHead(); $name = $form->user->name; $expense_number = $form->expense_number; - $tanggal = $form->created_at; + $tanggal = $form->tanggal; $total = $form->total; - // Collect all recipients $recipients = [$form->user->email, auth()->user()->email]; $phoneNumbers = [$form->user->phone, auth()->user()->phone]; if ($heads['cabang_head']) { - $recipients[] = $heads['cabang_head']['email']; - $phoneNumbers[] = $heads['cabang_head']['phone']; + $recipients[] = $heads['cabang_head']['email']; + $phoneNumbers[] = $heads['cabang_head']['phone']; } if ($heads['region_head']) { - $recipients[] = $heads['region_head']['email']; - $phoneNumbers[] = $heads['region_head']['phone']; + $recipients[] = $heads['region_head']['email']; + $phoneNumbers[] = $heads['region_head']['phone']; } - // Remove duplicate email addresses, if any $recipients = array_unique($recipients); $phoneNumbers = array_unique($phoneNumbers); - // send whatsapp message + // Route khusus Entertainment $url = route('forms.entertainment.view', $form->id); - WhatsappHelper::rejectExpense($phoneNumbers, $expense_number, $url, $tanggal, $total, $name, $request->remarks); + + // Notifikasi WhatsApp + $this->rejectExpenseWithNotification($phoneNumbers, $expense_number, $url, $tanggal, $total, $name, $request->remarks, $recipients); - // Send bulk email - $brevoService = app(BrevoService::class); - foreach ($recipients as $recipient) { - try { - $mail = new ExpenseRejected( - $name, - $expense_number, - $tanggal, - $total, - $url, - $request->remarks - ); - - $response = $brevoService->expenseRejected($recipient, $name, $mail); - - if (isset($response['success']) && $response['success'] === false) { - Log::warning("Email to {$recipient} failed: " . ($response['message'] ?? 'Unknown error')); - } - } catch (\Exception $e) { - Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); - continue; - } - } - - AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Entertainment (' . $form->expense_number . ')'); + // Audit Trail Khusus Form Entertainment + AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Entertainment Presentation (' . $form->expense_number . ') oleh ' . auth()->user()->name); + session()->flash('success', 'Form has been rejected.'); return redirect()->back(); } + private function rejectExpenseWithNotification($phoneNumbers, $expense_number, $url, $tanggal, $total, $name, $remarks, $recipients) + { + WhatsappHelper::rejectExpense($phoneNumbers, $expense_number, $url, $tanggal, $total, $name, $remarks); + + // Notifikasi Email (Brevo) + $brevoService = app(BrevoService::class); + foreach ($recipients as $recipient) { + try { + $mail = new ExpenseRejected($name, $expense_number, $tanggal, $total, $url, $remarks); + $brevoService->expenseRejected($recipient, $name, $mail); + } catch (\Exception $e) { + Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); + continue; + } + } + } + public function open($id) { $this->checkAuthorization(auth()->user(), ['final_approval.open']); @@ -935,23 +968,23 @@ class FormEntertainmentPresentationController extends Controller return redirect()->back(); } - public function destroy($id) - { + public function destroy($id) + { $this->checkAuthorization(auth()->user(), ['forms.entertainment.delete']); $role = auth()->user()->getRoleNames()[0]; - $form = FormEntertaimentPresentation::findOrfail($id); + $form = FormEntertaimentPresentation::findOrfail($id); if($form->status != 'On Progress' && $role == 'Medical Representatif') { session()->flash('error', 'You cannot edit this form because it has been approved or rejected.'); return redirect()->back(); } AuditTrailHelper::AddAuditTrail('Delete', 'Delete Record at Form Entertainment Presentation (' . $form->expense_number . ')'); - $form->delete(); + $form->delete(); - session()->flash('success', 'Form has been deleted.'); - return redirect()->back(); - } + session()->flash('success', 'Form has been deleted.'); + return redirect()->back(); + } protected function addAttachmentValidationRules(\Illuminate\Validation\Validator $validator, Request $request): void { @@ -1138,4 +1171,4 @@ class FormEntertainmentPresentationController extends Controller return ucwords(str_replace('_', ' ', $category)); } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/Forms/FormMeetingSeminarController.php b/app/Http/Controllers/Forms/FormMeetingSeminarController.php index 2512913..ed88d6b 100644 --- a/app/Http/Controllers/Forms/FormMeetingSeminarController.php +++ b/app/Http/Controllers/Forms/FormMeetingSeminarController.php @@ -75,7 +75,7 @@ class FormMeetingSeminarController extends Controller $role = auth()->user()->getRoleNames()[0]; $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $closingDay = (int) env('CLOSING_DATE', 13); $userRegionData = auth()->user()->getMyCabangAndRegionId(); $regionContextId = $userRegionData['region'] !== '-' ? $userRegionData['region'] : null; $cabangContextId = $userRegionData['cabang'] !== '-' ? $userRegionData['cabang'] : null; @@ -180,14 +180,14 @@ class FormMeetingSeminarController extends Controller ]); } - public function create() - { + public function create() + { $this->checkAuthorization(auth()->user(), ['forms.meeting.create']); - return view('backend.pages.forms.meeting.create'); - } + return view('backend.pages.forms.meeting.create'); + } - public function store(Request $request) + public function store(Request $request) { $this->checkAuthorization(auth()->user(), ['forms.meeting.create']); @@ -204,23 +204,28 @@ class FormMeetingSeminarController extends Controller $validator->validate(); $tanggal = Carbon::parse($request->tanggal); + + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); + $now = Carbon::now(); // Tentukan periode aktif berdasarkan tanggal saat ini if ($now->day >= $startDay) { - $startDate = Carbon::create($now->year, $now->month, $startDay); - $closingDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $closingDay); + // Jendela Aktif: Tgl 11 Bulan Ini s/d Tgl 10 Bulan Depan + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); } else { - $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay); - $closingDate = Carbon::create($now->year, $now->month, $closingDay); + // Jendela Aktif: Tgl 11 Bulan Lalu s/d Tgl 10 Bulan Ini + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); } - // Validasi apakah tanggal pengajuan masuk ke periode saat ini - if ($tanggal->lt($startDate) || $tanggal->gt($closingDate)) { - session()->flash('error', "Gagal, tanggal tidak berada dalam periode input: {$startDate->format('d M')} - {$closingDate->format('d M')}"); - return redirect()->back(); + // Validasi apakah tanggal pengajuan masuk ke periode input saat ini + if ($tanggal->lt($startDate) || $tanggal->gt($maxInputDate)) { + session()->flash('error', "Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: {$startDate->format('d M Y')} - {$maxInputDate->format('d M Y')}"); + return redirect()->back()->withInput(); } $cabang = UserHasCabang::with('cabang')->where('user_id', auth()->user()->id)->first()?->cabang; @@ -230,7 +235,9 @@ class FormMeetingSeminarController extends Controller } $region = Region::find($cabang->region_id); - $kategori = Kategori::where('name', 'Meeting / Seminar')->first(); + + // Optimasi pencarian kategori menggunakan LIKE fleksibel untuk menjamin ID ditemukan + $kategori = Kategori::where('name', 'LIKE', '%Meeting / Seminar%')->first(); // Hitung periode dari tanggal input (bukan dari today) if ($tanggal->day >= $startDay) { @@ -266,7 +273,7 @@ class FormMeetingSeminarController extends Controller // Check if the total nominal exceeds the available budget if ($totalNominal > $availableBudget) { session()->flash('error', 'Alokasi budget bulanan cabang tidak mencukupi,silahkan ajukan pada periode expense berikutnya'); - return redirect()->back(); + return redirect()->back()->withInput(); } DB::beginTransaction(); @@ -276,6 +283,7 @@ class FormMeetingSeminarController extends Controller $form = FormMeetingSeminar::create([ 'expense_number' => $expense_number, 'user_id' => auth()->user()->id, + 'cabang_id' => $cabang->id, // <--- REPARASI SUNTIK CABANG_ID BARU 'tanggal' => $request->tanggal, 'allowance' => $data_nominal['allowance'], 'transport_ankot' => $data_nominal['transport_ankot'], @@ -285,6 +293,7 @@ class FormMeetingSeminarController extends Controller 'bukti_transport_ankot' => null, 'bukti_hotel' => null, 'account_number' => $kategori?->account_number, + 'kategori_id' => $kategori->id ?? null, 'status' => 'On Progress', ]); @@ -391,10 +400,10 @@ class FormMeetingSeminarController extends Controller AuditTrailHelper::AddAuditTrail('Insert', 'Insert New Expense at Form Meeting Seminar (' . $expense_number . ')'); session()->flash('success', 'Form has been created.'); return redirect()->back(); - } + } - public function edit($id) - { + public function edit($id) + { $this->checkAuthorization(auth()->user(), ['forms.meeting.edit']); $role = auth()->user()->getRoleNames()[0]; @@ -406,14 +415,14 @@ class FormMeetingSeminarController extends Controller $attachments = $this->formatAttachmentCollection($form); - return view('backend.pages.forms.meeting.edit', [ + return view('backend.pages.forms.meeting.edit', [ 'form' => $form, 'attachments' => $attachments, - ]); - } + ]); + } - public function update(Request $request, $id) - { + public function update(Request $request, $id) + { $this->checkAuthorization(auth()->user(), ['forms.meeting.edit']); $role = auth()->user()->getRoleNames()[0]; $form = FormMeetingSeminar::with('attachments')->findOrFail($id); @@ -434,6 +443,28 @@ class FormMeetingSeminarController extends Controller session()->flash('error', 'You cannot edit this form because it has been approved or rejected.'); return redirect()->back(); } + + $tanggal = Carbon::parse($request->tanggal); + + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE + $startDay = (int) env('STARTING_DATE', 11); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); + + $now = Carbon::now(); + + if ($now->day >= $startDay) { + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); + } else { + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); + } + + // Validasi apakah tanggal pengajuan masuk ke periode input saat ini + if ($tanggal->lt($startDate) || $tanggal->gt($maxInputDate)) { + session()->flash('error', "Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: {$startDate->format('d M Y')} - {$maxInputDate->format('d M Y')}"); + return redirect()->back()->withInput(); + } $cabang = UserHasCabang::with('cabang')->where('user_id', $form->user_id)->first()?->cabang; if (!$cabang) { @@ -442,7 +473,7 @@ class FormMeetingSeminarController extends Controller } $region = Region::find($cabang->region_id); - $kategori = Kategori::where('name', 'Meeting / Seminar')->first(); + $kategori = Kategori::where('name', 'LIKE', '%Meeting / Seminar%')->first(); $folderPath = '/expense/' . $region->code . '/' . $cabang->code . '/meeting'; @@ -458,8 +489,6 @@ class FormMeetingSeminarController extends Controller $totalNominal = array_sum($data_nominal); // Hitung periode dari tanggal form yang diinput - $tanggal = Carbon::parse($request->tanggal); - $startDay = (int) env('STARTING_DATE', 11); if ($tanggal->day >= $startDay) { $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); } else { @@ -473,18 +502,20 @@ class FormMeetingSeminarController extends Controller $availableBudget = BudgetHelper::getAvailableBudget($cabang->id, $periodeMonth, $periodeYear); if ($totalNominal > $availableBudget) { session()->flash('error', 'The total nominal exceeds the available budget.'); - return redirect()->back(); + return redirect()->back()->withInput(); } DB::beginTransaction(); try { $form->update([ + 'cabang_id' => $cabang->id, // <--- REPARASI SUNTIK CABANG_ID TERBARU SAAT PROSES UPDATE 'tanggal' => $request->tanggal, 'allowance' => $data_nominal['allowance'], 'transport_ankot' => $data_nominal['transport_ankot'], 'hotel' => $data_nominal['hotel'], 'total' => $totalNominal, 'account_number' => $kategori?->account_number, + 'kategori_id' => $kategori->id ?? null, ]); if (!empty($attachmentsPayload)) { @@ -512,7 +543,7 @@ class FormMeetingSeminarController extends Controller AuditTrailHelper::AddAuditTrail('Update', 'Update Expense at Form Meeting Seminar (' . $form->expense_number . ')'); session()->flash('success', 'Form has been updated.'); return redirect()->back(); - } + } public function deleteAttachment(Request $request, $formId, $attachmentId) { @@ -735,7 +766,8 @@ class FormMeetingSeminarController extends Controller return redirect()->back(); } - public function finalApprove($id) + // <--- REPARASI SUNTIKAN OBJECT REQUEST HASIL CHECKLIST JAVASCRIPT MODAL DILUAR + public function finalApprove(Request $request, $id) { $this->checkAuthorization(auth()->user(), ['final_approval.approve']); $form = FormMeetingSeminar::findOrFail($id); @@ -764,22 +796,27 @@ class FormMeetingSeminarController extends Controller $periodeMonth = strtolower($periodeDate->format('F')); $periodeYear = (int) $periodeDate->format('Y'); + // <--- REPARASI: Penangkapan nilai payload dari checkbox JavaScript + $approvedTotal = $request->input('approved_total', $form->total); + + // Ambil sisa budget bulanan dan sinkronkan dengan nominal dinamis terbaru $availableBudget = BudgetHelper::getAvailableBudget($cabang_id, $periodeMonth, $periodeYear); - if($form->approved_total > $availableBudget) { + if($approvedTotal > $availableBudget) { session()->flash('error', 'Budget cabang untuk periode ini tidak mencukupi, silahkan ajukan pada periode expense berikutnya'); return redirect()->back(); } $form->update([ + 'approved_total' => $approvedTotal, // <--- SUNTIK NOMINAL BARU HASIL CHECKLIST DARI JAVASCRIPT KE DB 'final_approved_at' => now(), 'final_approved_by' => auth()->user()->id, - 'status' => 'Closed', + 'status' => 'Closed', ]); $name = $form->user->name; $expense_number = $form->expense_number; $tanggal = $form->tanggal; - $total = $form->approved_total; + $total = $form->approved_total; // <--- TOTAL BARU YANG DIKIRIMKAN KE EMAIL / WHATSAPP $roles = ['Head of Sales Marketing', 'Marketing Operational Manager Region']; $recipients = [$form->user->email, auth()->user()->email]; @@ -822,16 +859,18 @@ class FormMeetingSeminarController extends Controller $url = route('forms.meeting.view', $form->id); WhatsappHelper::finalApprove($phoneNumbers, $expense_number, $url, $tanggal, $total, $name); - // Send bulk email + // Send bulk email (Optimasi menggunakan BrevoService) + $brevoService = app(BrevoService::class); foreach ($recipients as $recipient) { try { - Mail::to($recipient)->send(new FinalApprove( + $mail = new FinalApprove( $name, $expense_number, $tanggal, $total, $url - )); + ); + $brevoService->expenseFinalApproved($recipient, $name, $mail); } catch (\Exception $e) { Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); continue; @@ -845,16 +884,22 @@ class FormMeetingSeminarController extends Controller public function reject(Request $request, $id) { - $this->checkAuthorization(auth()->user(), ['approval.reject']); + if (!auth()->user()->hasAnyPermission(['approval.reject', 'approval2.reject', 'final_approval.approve'])) { + abort(403, 'Akses Ditolak: Anda tidak memiliki izin untuk melakukan Reject.'); + } $request->validate([ - 'remarks' => 'required|string|max:500', + 'remarks' => 'required|string|max:500', ]); $form = FormMeetingSeminar::findOrfail($id); + + // Update Metadata Rejeksi $form->update([ - 'status' => 'Rejected', - 'remarks' => $request->remarks, + 'status' => 'Rejected', + 'remarks' => $request->remarks, + 'rejected_by' => auth()->id(), // Kolom baru + 'rejected_at' => now(), // Kolom baru ]); $heads = $form->user->getCabangAndRegionHead(); @@ -863,53 +908,37 @@ class FormMeetingSeminarController extends Controller $tanggal = $form->tanggal; $total = $form->total; - // Collect all recipients $recipients = [$form->user->email, auth()->user()->email]; $phoneNumbers = [$form->user->phone, auth()->user()->phone]; if ($heads['cabang_head']) { - $recipients[] = $heads['cabang_head']['email']; - $phoneNumbers[] = $heads['cabang_head']['phone']; + $recipients[] = $heads['cabang_head']['email']; + $phoneNumbers[] = $heads['cabang_head']['phone']; } if ($heads['region_head']) { - $recipients[] = $heads['region_head']['email']; - $phoneNumbers[] = $heads['region_head']['phone']; + $recipients[] = $heads['region_head']['email']; + $phoneNumbers[] = $heads['region_head']['phone']; } - // Remove duplicate email addresses, if any $recipients = array_unique($recipients); $phoneNumbers = array_unique($phoneNumbers); - // send whatsapp message $url = route('forms.meeting.view', $form->id); WhatsappHelper::rejectExpense($phoneNumbers, $expense_number, $url, $tanggal, $total, $name, $request->remarks); - // Send bulk email $brevoService = app(BrevoService::class); foreach ($recipients as $recipient) { - try { - $mail = new ExpenseRejected( - $name, - $expense_number, - $tanggal, - $total, - $url, - $request->remarks - ); - - $response = $brevoService->expenseRejected($recipient, $name, $mail); - - if (isset($response['success']) && $response['success'] === false) { - Log::warning("Email to {$recipient} failed: " . ($response['message'] ?? 'Unknown error')); + try { + $mail = new ExpenseRejected($name, $expense_number, $tanggal, $total, $url, $request->remarks); + $brevoService->expenseRejected($recipient, $name, $mail); + } catch (\Exception $e) { + Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); + continue; } - } catch (\Exception $e) { - Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); - continue; - } } - AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Meeting Seminar (' . $form->expense_number . ')'); + AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Meeting Seminar (' . $form->expense_number . ') oleh ' . auth()->user()->name); session()->flash('success', 'Form has been rejected.'); return redirect()->back(); } @@ -930,12 +959,12 @@ class FormMeetingSeminarController extends Controller return redirect()->back(); } - public function destroy($id) - { + public function destroy($id) + { $this->checkAuthorization(auth()->user(), ['forms.meeting.delete']); $role = auth()->user()->getRoleNames()[0]; - $form = FormMeetingSeminar::findOrfail($id); + $form = FormMeetingSeminar::findOrfail($id); if($form->status != 'On Progress' && $role == 'Medical Representatif') { session()->flash('error', 'You cannot edit this form because it has been approved or rejected.'); return redirect()->back(); @@ -943,11 +972,11 @@ class FormMeetingSeminarController extends Controller AuditTrailHelper::AddAuditTrail('Delete', 'Delete Record at Form Meeting Seminar (' . $form->expense_number . ')'); - $form->delete(); + $form->delete(); - session()->flash('success', 'Form has been deleted.'); - return redirect()->back(); - } + session()->flash('success', 'Form has been deleted.'); + return redirect()->back(); + } protected function addAttachmentValidationRules(\Illuminate\Validation\Validator $validator, Request $request): void { @@ -1138,4 +1167,4 @@ class FormMeetingSeminarController extends Controller return ucwords(str_replace('_', ' ', $category)); } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/Forms/FormOtherController.php b/app/Http/Controllers/Forms/FormOtherController.php index 906af43..604113d 100644 --- a/app/Http/Controllers/Forms/FormOtherController.php +++ b/app/Http/Controllers/Forms/FormOtherController.php @@ -74,7 +74,7 @@ class FormOtherController extends Controller $role = auth()->user()->getRoleNames()[0]; $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $closingDay = (int) env('CLOSING_DATE', 13); $userRegionData = auth()->user()->getMyCabangAndRegionId(); $regionContextId = $userRegionData['region'] !== '-' ? $userRegionData['region'] : null; $cabangContextId = $userRegionData['cabang'] !== '-' ? $userRegionData['cabang'] : null; @@ -179,18 +179,18 @@ class FormOtherController extends Controller ]); } - public function create() - { + public function create() + { $this->checkAuthorization(auth()->user(), ['forms.other.create']); - return view('backend.pages.forms.other.create', [ + return view('backend.pages.forms.other.create', [ 'kategori' => Kategori::whereNotIn('name', $this->excludedKategoriNames)->get(), 'attachmentCategories' => $this->attachmentCategories, ]); - } + } - public function store(Request $request) - { + public function store(Request $request) + { $this->checkAuthorization(auth()->user(), ['forms.other.create']); $validator = Validator::make($request->all(), [ 'kategori_id' => 'required', @@ -203,7 +203,7 @@ class FormOtherController extends Controller 'nullable', 'file', 'max:10240', - 'mimetypes:image/jpeg,image/png,image/webp,application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/plain,text/csv,application/zip,application/x-zip-compressed,application/x-7z-compressed,application/vnd.rar,application/octet-stream' + 'mimetypes:image/jpeg,image/png,image/webp,application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/plain,text/csv,application/zip,application/x-zip-compressed,application/x-7z-compressed,application/vnd.rar,application/octet-stream' ], ]); @@ -229,23 +229,27 @@ class FormOtherController extends Controller $validator->validate(); $tanggal = Carbon::parse($request->tanggal); + + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); $now = Carbon::now(); - // Tentukan periode input berdasarkan waktu saat ini + // Tentukan periode aktif berdasarkan tanggal saat ini if ($now->day >= $startDay) { - $startDate = Carbon::create($now->year, $now->month, $startDay); - $closingDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $closingDay); + // Jendela Aktif: Tgl 11 Bulan Ini s/d Tgl 10 Bulan Depan + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); } else { - $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay); - $closingDate = Carbon::create($now->year, $now->month, $closingDay); + // Jendela Aktif: Tgl 11 Bulan Lalu s/d Tgl 10 Bulan Ini + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); } - // Validasi apakah tanggal berada dalam periode aktif input - if ($tanggal->lt($startDate) || $tanggal->gt($closingDate)) { - session()->flash('error', "Gagal, tanggal tidak berada dalam periode input: {$startDate->format('d M')} - {$closingDate->format('d M')}"); - return redirect()->back(); + // Validasi apakah tanggal pengajuan masuk ke periode input saat ini + if ($tanggal->lt($startDate) || $tanggal->gt($maxInputDate)) { + session()->flash('error', "Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: {$startDate->format('d M Y')} - {$maxInputDate->format('d M Y')}"); + return redirect()->back()->withInput(); } // Ambil cabang user @@ -256,7 +260,7 @@ class FormOtherController extends Controller return redirect()->back(); } - // Tentukan periode budget berdasarkan tanggal input (bukan now) + // Tentukan periode budget berdasarkan tanggal input if ($tanggal->day >= $startDay) { $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); } else { @@ -270,7 +274,7 @@ class FormOtherController extends Controller $availableBudget = BudgetHelper::getAvailableBudget($cabang->id, $periodeMonth, $periodeYear); if($request->total > $availableBudget) { session()->flash('error', 'Alokasi budget bulanan cabang tidak mencukupi,silahkan ajukan pada periode expense berikutnya'); - return redirect()->back(); + return redirect()->back()->withInput(); } $region = Region::where('id', $cabang->region_id)->first(); @@ -279,10 +283,9 @@ class FormOtherController extends Controller $folderPath = '/expense/' . $region->code . '/' . $cabang->code . '/other'; $attachmentsPayload = $this->buildAttachmentPayload($request, $folderPath); $primaryAttachmentPath = $attachmentsPayload[0]['file_path'] ?? null; - $primaryAttachmentPath = $attachmentsPayload[0]['file_path'] ?? null; // Generate sequence number and expense number - $sequence_number = FormOthers::withTrashed()->where('expense_number', 'like', $region->code . '-' . $cabang->code . '-OTH-' . date('ym') . '%')->count() + 1; + $sequence_number = FormOthers::withTrashed()->where('expense_number', 'like', $region->code . '-' . $cabang->code . '-OTH-' . date('ym') . '%')->count() + 1; $formatted_sequence_number = str_pad($sequence_number, 4, '0', STR_PAD_LEFT); $expense_number = $region->code . '-' . $cabang->code . '-OTH-' . date('ym') . $formatted_sequence_number; @@ -291,6 +294,7 @@ class FormOtherController extends Controller $form = FormOthers::create([ 'expense_number' => $expense_number, 'user_id' => auth()->user()->id, + 'cabang_id' => $cabang->id, // <--- REPARASI SUNTIK CABANG_ID BARU 'kategori_id' => $request->kategori_id, 'tanggal' => $request->tanggal, 'keterangan' => $request->keterangan, @@ -400,10 +404,10 @@ class FormOtherController extends Controller AuditTrailHelper::AddAuditTrail('Insert', 'Insert New Expense at Form Other (' . $expense_number . ')'); session()->flash('success', 'Form has been created.'); return redirect()->back(); - } + } - public function edit($id) - { + public function edit($id) + { $this->checkAuthorization(auth()->user(), ['forms.other.edit']); $role = auth()->user()->getRoleNames()[0]; @@ -413,15 +417,15 @@ class FormOtherController extends Controller return redirect()->back(); } - return view('backend.pages.forms.other.edit', [ + return view('backend.pages.forms.other.edit', [ 'form' => $form, 'kategori' => Kategori::whereNotIn('name', $this->excludedKategoriNames)->get(), 'attachments' => $this->formatAttachmentCollection($form), 'attachmentCategories' => $this->attachmentCategories, ]); - } + } - public function update(Request $request, $id) + public function update(Request $request, $id) { $this->checkAuthorization(auth()->user(), ['forms.other.edit']); $role = auth()->user()->getRoleNames()[0]; @@ -438,10 +442,28 @@ class FormOtherController extends Controller return redirect()->back(); } - // Hitung periode dari tanggal input $tanggal = Carbon::parse($request->tanggal); + + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE $startDay = (int) env('STARTING_DATE', 11); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); + $now = Carbon::now(); + if ($now->day >= $startDay) { + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); + } else { + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); + } + + // Validasi apakah tanggal pengajuan masuk ke periode input saat ini + if ($tanggal->lt($startDate) || $tanggal->gt($maxInputDate)) { + session()->flash('error', "Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: {$startDate->format('d M Y')} - {$maxInputDate->format('d M Y')}"); + return redirect()->back()->withInput(); + } + + // Hitung periode budget dari tanggal input if ($tanggal->day >= $startDay) { $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); } else { @@ -465,7 +487,7 @@ class FormOtherController extends Controller 'nullable', 'file', 'max:10240', - 'mimetypes:image/jpeg,image/png,image/webp,application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/plain,text/csv,application/zip,application/x-zip-compressed,application/x-7z-compressed,application/vnd.rar,application/octet-stream' + 'mimetypes:image/jpeg,image/png,image/webp,application/pdf,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/plain,text/csv,application/zip,application/x-zip-compressed,application/x-7z-compressed,application/vnd.rar,application/octet-stream' ], ]); @@ -495,10 +517,12 @@ class FormOtherController extends Controller $folderPath = '/expense/' . $region->code . '/' . $cabang->code . '/other'; $attachmentsPayload = $this->buildAttachmentPayload($request, $folderPath); + $primaryAttachmentPath = !empty($attachmentsPayload) ? $attachmentsPayload[0]['file_path'] : null; DB::beginTransaction(); try { $form->update([ + 'cabang_id' => $cabang->id, // <--- REPARASI SUNTIK CABANG_ID TERBARU 'kategori_id' => $request->kategori_id, 'tanggal' => $request->tanggal, 'keterangan' => $request->keterangan, @@ -752,7 +776,8 @@ class FormOtherController extends Controller return redirect()->back(); } - public function finalApprove($id) + // <--- REPARASI SUNTIKAN OBJECT REQUEST HASIL CHECKLIST JAVASCRIPT MODAL DILUAR + public function finalApprove(Request $request, $id) { $this->checkAuthorization(auth()->user(), ['final_approval.approve']); $form = FormOthers::findOrFail($id); @@ -781,14 +806,18 @@ class FormOtherController extends Controller $periodeMonth = strtolower($periodeDate->format('F')); $periodeYear = (int) $periodeDate->format('Y'); - // Ambil sisa budget + // <--- REPARASI: Penangkapan nilai payload dari checkbox JavaScript + $approvedTotal = $request->input('approved_total', $form->total); + + // Ambil sisa budget dan sinkronkan validasinya dengan nominal dinamis terbaru $availableBudget = BudgetHelper::getAvailableBudget($cabang_id, $periodeMonth, $periodeYear); - if($form->approved_total > $availableBudget) { + if($approvedTotal > $availableBudget) { session()->flash('error', 'Budget cabang untuk periode ini tidak mencukupi, silahkan ajukan pada periode expense berikutnya'); return redirect()->back(); } $form->update([ + 'approved_total' => $approvedTotal, // <--- SUNTIK NOMINAL BARU HASIL CHECKLIST DARI JAVASCRIPT KE DB 'final_approved_at' => now(), 'final_approved_by' => auth()->user()->id, 'status' => 'Closed', @@ -797,7 +826,7 @@ class FormOtherController extends Controller $name = $form->user->name; $expense_number = $form->expense_number; $tanggal = $form->created_at; - $total = $form->approved_total; + $total = $form->approved_total; // <--- SEKARANG MENGGUNAKAN DATA UPDATE TERBARU $roles = ['Head of Sales Marketing', 'Marketing Operational Manager Region']; $recipients = [$form->user->email, auth()->user()->email]; @@ -870,71 +899,60 @@ class FormOtherController extends Controller public function reject(Request $request, $id) { - $this->checkAuthorization(auth()->user(), ['approval.reject']); + if (!auth()->user()->hasAnyPermission(['approval.reject', 'approval2.reject', 'final_approval.approve'])) { + abort(403, 'Akses Ditolak: Anda tidak memiliki izin untuk melakukan Reject.'); + } $request->validate([ - 'remarks' => 'required|string|max:500', + 'remarks' => 'required|string|max:500', ]); $form = FormOthers::findOrfail($id); + $form->update([ - 'status' => 'Rejected', - 'remarks' => $request->remarks, + 'status' => 'Rejected', + 'remarks' => $request->remarks, + 'rejected_by' => auth()->id(), // Kolom baru + 'rejected_at' => now(), // Kolom baru ]); $heads = $form->user->getCabangAndRegionHead(); $name = $form->user->name; $expense_number = $form->expense_number; - $tanggal = $form->created_at; + $tanggal = $form->tanggal; $total = $form->total; - // Collect all recipients $recipients = [$form->user->email, auth()->user()->email]; $phoneNumbers = [$form->user->phone, auth()->user()->phone]; if ($heads['cabang_head']) { - $recipients[] = $heads['cabang_head']['email']; - $phoneNumbers[] = $heads['cabang_head']['phone']; + $recipients[] = $heads['cabang_head']['email']; + $phoneNumbers[] = $heads['cabang_head']['phone']; } if ($heads['region_head']) { - $recipients[] = $heads['region_head']['email']; - $phoneNumbers[] = $heads['region_head']['phone']; + $recipients[] = $heads['region_head']['email']; + $phoneNumbers[] = $heads['region_head']['phone']; } - // Remove duplicate email addresses, if any $recipients = array_unique($recipients); $phoneNumbers = array_unique($phoneNumbers); - // send whatsapp message - $url = route('forms.other.view', $form->id); + $url = route('forms.others.view', $form->id); WhatsappHelper::rejectExpense($phoneNumbers, $expense_number, $url, $tanggal, $total, $name, $request->remarks); - // Send bulk email $brevoService = app(BrevoService::class); foreach ($recipients as $recipient) { - try { - $mail = new ExpenseRejected( - $name, - $expense_number, - $tanggal, - $total, - $url, - $request->remarks - ); - - $response = $brevoService->expenseRejected($recipient, $name, $mail); - - if (isset($response['success']) && $response['success'] === false) { - Log::warning("Email to {$recipient} failed: " . ($response['message'] ?? 'Unknown error')); + try { + $mail = new ExpenseRejected($name, $expense_number, $tanggal, $total, $url, $request->remarks); + $brevoService->expenseRejected($recipient, $name, $mail); + } catch (\Exception $e) { + Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); + continue; } - } catch (\Exception $e) { - Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); - continue; - } } - AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Other (' . $form->expense_number . ')'); + AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Others (' . $form->expense_number . ') oleh ' . auth()->user()->name); session()->flash('success', 'Form has been rejected.'); return redirect()->back(); } @@ -955,12 +973,12 @@ class FormOtherController extends Controller return redirect()->back(); } - public function destroy($id) - { + public function destroy($id) + { $this->checkAuthorization(auth()->user(), ['forms.other.delete']); $role = auth()->user()->getRoleNames()[0]; - $form = FormOthers::findOrfail($id); + $form = FormOthers::findOrfail($id); if($form->status != 'On Progress' && $role == 'Medical Representatif') { session()->flash('error', 'You cannot edit this form because it has been approved or rejected.'); return redirect()->back(); @@ -968,11 +986,11 @@ class FormOtherController extends Controller AuditTrailHelper::AddAuditTrail('Delete', 'Delete Record at Form Other (' . $form->expense_number . ')'); - $form->delete(); + $form->delete(); - session()->flash('success', 'Form has been deleted.'); - return redirect()->back(); - } + session()->flash('success', 'Form has been deleted.'); + return redirect()->back(); + } protected function formatAttachmentCollection(FormOthers $form): array { @@ -1075,4 +1093,4 @@ class FormOtherController extends Controller return $payload; } -} +} \ No newline at end of file diff --git a/app/Http/Controllers/Forms/FormUpCountryController.php b/app/Http/Controllers/Forms/FormUpCountryController.php index 9e6de0a..dc51a9c 100644 --- a/app/Http/Controllers/Forms/FormUpCountryController.php +++ b/app/Http/Controllers/Forms/FormUpCountryController.php @@ -66,7 +66,7 @@ class FormUpCountryController extends Controller $role = auth()->user()->getRoleNames()[0]; $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $closingDay = (int) env('CLOSING_DATE', 13); // Current time reference $now = Carbon::now(); @@ -190,20 +190,20 @@ class FormUpCountryController extends Controller ]); } - public function create() - { + public function create() + { $this->checkAuthorization(auth()->user(), ['forms.country.create']); $rayons = $this->getAccessibleRayonsForCurrentUser(); - return view('backend.pages.forms.upcountry.create', [ - 'rayons' => $rayons, + return view('backend.pages.forms.upcountry.create', [ + 'rayons' => $rayons, 'attachmentCategories' => array_keys($this->attachmentCategories), 'attachmentCategoryLabels' => $this->attachmentCategories, - ]); - } + ]); + } - public function store(Request $request) - { + public function store(Request $request) +{ $this->checkAuthorization(auth()->user(), ['forms.country.create']); $request->validate([ 'rayon_id' => 'required|exists:rayon,id', @@ -221,23 +221,24 @@ class FormUpCountryController extends Controller ]); $tanggal = Carbon::parse($request->tanggal); + + // SINKRONISASI JENDELA INPUT ATURAN BISNIS: 11 s/d 10 Bulan Berikutnya $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); $now = Carbon::now(); - // Tentukan periode aktif saat ini if ($now->day >= $startDay) { - $startDate = Carbon::create($now->year, $now->month, $startDay); - $closingDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $closingDay); + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); } else { - $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay); - $closingDate = Carbon::create($now->year, $now->month, $closingDay); + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); } - // Validasi apakah tanggal input berada di periode aktif - if ($tanggal->lt($startDate) || $tanggal->gt($closingDate)) { - session()->flash('error', "Gagal, tanggal tidak berada dalam periode input: {$startDate->format('d M')} - {$closingDate->format('d M')}"); - return redirect()->back(); + // Blokade Sistem: Tolak input jika berada di luar siklus pengisian aktif + if ($now->lt($startDate) || $now->gt($maxInputDate)) { + session()->flash('error', "Gagal, pengajuan saat ini sudah ditutup. Periode pengisian aktif hanya diizinkan dari tanggal {$startDay} sampai tanggal {$inputMaxDay} berikutnya."); + return redirect()->back()->withInput(); } $cabang = UserHasCabang::with('cabang')->where('user_id', auth()->user()->id)->first()?->cabang; @@ -247,7 +248,9 @@ class FormUpCountryController extends Controller } $region = Region::find($cabang->region_id); - $kategori = Kategori::where('name', 'Up Country')->first(); + + // Optimasi pencarian kategori menggunakan LIKE fleksibel untuk menjamin ID ditemukan + $kategori = Kategori::where('name', 'LIKE', '%Up Country%')->first(); // Hitung periode budget dari tanggal input if ($tanggal->day >= $startDay) { @@ -301,6 +304,8 @@ class FormUpCountryController extends Controller $form = FormUpCountry::create([ 'expense_number' => $expense_number, 'user_id' => auth()->user()->id, + 'cabang_id' => $cabang->id, // <--- REPARASI: Memasukkan Cabang ID milik user pengaju secara instan + 'kategori_id' => $kategori->id ?? null, // SUNTIK KATEGORI_ID OTOMATIS BERHASIL 'rayon_id' => $request->rayon_id, 'tanggal' => $request->tanggal, 'tujuan' => $request->tujuan, @@ -315,14 +320,14 @@ class FormUpCountryController extends Controller 'bukti_transport_ankot' => null, 'bukti_hotel' => null, 'uc_plan' => $ucPlanPath, - 'account_number' => $kategori->account_number, + 'account_number' => $kategori->account_number ?? null, 'status' => 'On Progress', ]); if (!empty($attachmentsPayload)) { $this->attachmentService->addMultipleAttachments( $form->id, - AttachmentTableName::FORM_UP_COUNTRY, + $dynamicTable = AttachmentTableName::FORM_UP_COUNTRY, $attachmentsPayload ); } @@ -359,11 +364,11 @@ class FormUpCountryController extends Controller foreach ($roles as $role) { $users = Admin::getUserByRoleName($role)->filter(function ($user) use ($role, $cabang_id, $region_id) { if ($role === 'Marketing Information System') { - return true; // Tidak ada filter cabang untuk peran ini + return true; } if (!$user->cabang || !$user->cabang->cabang) { - return false; // Pastikan user memiliki cabang + return false; } if ($role === 'Admin Region' || $role === 'Marketing Operational Manager Region') { @@ -417,10 +422,10 @@ class FormUpCountryController extends Controller AuditTrailHelper::AddAuditTrail('Insert', 'Insert New Expense at Form Up Country (' . $expense_number . ')'); session()->flash('success', 'Form has been created.'); return redirect()->back(); - } +} - public function edit($id) - { + public function edit($id) + { $this->checkAuthorization(auth()->user(), ['forms.country.edit']); $role = auth()->user()->getRoleNames()[0]; @@ -453,14 +458,14 @@ class FormUpCountryController extends Controller ]; })->values(); - return view('backend.pages.forms.upcountry.edit', [ - 'rayons' => $rayonData, + return view('backend.pages.forms.upcountry.edit', [ + 'rayons' => $rayonData, 'form' => $form, 'attachments' => $existingAttachments, 'attachmentCategories' => array_keys($this->attachmentCategories), 'attachmentCategoryLabels' => $this->attachmentCategories, - ]); - } + ]); + } protected function getAccessibleRayonsForCurrentUser() { @@ -494,7 +499,7 @@ class FormUpCountryController extends Controller } public function update(Request $request, $id) - { +{ $this->checkAuthorization(auth()->user(), ['forms.country.edit']); $role = auth()->user()->getRoleNames()[0]; @@ -525,12 +530,31 @@ class FormUpCountryController extends Controller return redirect()->back(); } + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE + $startDay = (int) env('STARTING_DATE', 11); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); + $now = Carbon::now(); + + if ($now->day >= $startDay) { + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); + } else { + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); + } + + if ($now->lt($startDate) || $now->gt($maxInputDate)) { + session()->flash('error', "Gagal, pengajuan saat ini sudah ditutup. Periode pengisian aktif hanya diizinkan dari tanggal {$startDay} sampai tanggal {$inputMaxDay} berikutnya."); + return redirect()->back()->withInput(); + } + $region = Region::find($cabang->region_id); - $kategori = Kategori::where('name', 'Up Country')->first(); + + // Optimasi pencarian kategori menggunakan LIKE fleksibel untuk menjamin ID ditemukan + $kategori = Kategori::where('name', 'LIKE', '%Up Country%')->first(); // Hitung periode dari tanggal input $tanggal = Carbon::parse($request->tanggal); - $startDay = (int) env('STARTING_DATE', 11); if ($tanggal->day >= $startDay) { $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); } else { @@ -564,7 +588,7 @@ class FormUpCountryController extends Controller // Validasi budget dengan periode $availableBudget = BudgetHelper::getAvailableBudget($cabang->id, $periodeMonth, $periodeYear); - if(array_sum($data_nominal) > $availableBudget) { + if($totalNominal > $availableBudget) { session()->flash('error', 'The total nominal exceeds the available budget.'); return redirect()->back(); } @@ -574,6 +598,8 @@ class FormUpCountryController extends Controller try { $form->update([ 'rayon_id' => $request->rayon_id, + 'cabang_id' => $cabang->id, // <--- REPARASI: Memastikan Cabang ID tersimpan akurat saat update data + 'kategori_id' => $kategori->id ?? null, // RE-SUNTIK KATEGORI_ID SAAT UPDATE 'tanggal' => $request->tanggal, 'tujuan' => $request->tujuan, 'jarak' => $request->jarak, @@ -583,7 +609,7 @@ class FormUpCountryController extends Controller 'hotel' => $data_nominal['hotel'], 'total' => $totalNominal, 'uc_plan' => $ucPlanPath ?? $form->uc_plan, - 'account_number' => $kategori->account_number, + 'account_number' => $kategori->account_number ?? $form->account_number, ]); if (!empty($attachmentsPayload)) { @@ -609,7 +635,7 @@ class FormUpCountryController extends Controller AuditTrailHelper::AddAuditTrail('Update', 'Update Record at Form Up Country (' . $form->expense_number . ')'); session()->flash('success', 'Form has been updated.'); return redirect()->back(); - } +} public function deleteAttachment(Request $request, $formId, $attachmentId) { @@ -717,11 +743,11 @@ class FormUpCountryController extends Controller foreach ($roles as $role) { $users = Admin::getUserByRoleName($role)->filter(function ($user) use ($role, $cabang_id, $region_id) { if ($role === 'Marketing Information System') { - return true; // Tidak ada filter cabang untuk peran ini + return true; } if (!$user->cabang || !$user->cabang->cabang) { - return false; // Pastikan user memiliki cabang + return false; } if ($role === 'Admin Region' || $role === 'Marketing Operational Manager Region') { @@ -808,11 +834,11 @@ class FormUpCountryController extends Controller foreach ($roles as $role) { $users = Admin::getUserByRoleName($role)->filter(function ($user) use ($role, $cabang_id, $region_id) { if ($role === 'Marketing Information System' || $role === 'Head of Sales Marketing') { - return true; // Tidak ada filter cabang untuk peran ini + return true; } if (!$user->cabang || !$user->cabang->cabang) { - return false; // Pastikan user memiliki cabang + return false; } if ($role === 'Admin Region' || $role === 'Marketing Operational Manager Region') { @@ -867,7 +893,7 @@ class FormUpCountryController extends Controller return redirect()->back(); } - public function finalApprove($id) + public function finalApprove(Request $request, $id) // <--- MENAMBAHKAN OBJECT REQUEST { $this->checkAuthorization(auth()->user(), ['final_approval.approve']); $form = FormUpCountry::findOrFail($id); @@ -877,42 +903,61 @@ class FormUpCountryController extends Controller return redirect()->back(); } - // Ambil cabang_id dari user yang mengisi form + // ATURAN BISNIS: Batas Akhir Approval adalah tanggal CLOSING_DATE (Bulan Depan tgl 13) + $closingDay = (int) env('CLOSING_DATE', 13); + $startDay = (int) env('STARTING_DATE', 11); + $now = Carbon::now(); + $tanggalForm = Carbon::parse($form->tanggal); + + // Hitung batas maksimal closing sesuai siklus transaksi form tersebut + if ($tanggalForm->day >= $startDay) { + $maxClosingDate = Carbon::create($tanggalForm->year, $tanggalForm->month, $closingDay)->addMonth()->endOfDay(); + } else { + $maxClosingDate = Carbon::create($tanggalForm->year, $tanggalForm->month, $closingDay)->endOfDay(); + } + + if ($now->gt($maxClosingDate)) { + session()->flash('error', "Gagal, Batas waktu maksimal approval (Closing Date) untuk pengajuan ini telah berakhir pada {$maxClosingDate->format('d M Y H:i')} WIB."); + return redirect()->back(); + } + $cabang_id = UserHasCabang::where('user_id', $form->user_id)->first()?->cabang_id; if (!$cabang_id) { session()->flash('error', 'Data cabang tidak ditemukan.'); return redirect()->back(); } - // Hitung periode berdasarkan tanggal form - $tanggal = Carbon::parse($form->tanggal); - $startDay = (int) env('STARTING_DATE', 11); - if ($tanggal->day >= $startDay) { - $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); + if ($tanggalForm->day >= $startDay) { + $periodeDate = Carbon::create($tanggalForm->year, $tanggalForm->month, $startDay); } else { - $periodeDate = Carbon::create($tanggal->copy()->subMonth()->year, $tanggal->copy()->subMonth()->month, $startDay); + $periodeDate = Carbon::create($tanggalForm->copy()->subMonth()->year, $tanggalForm->copy()->subMonth()->month, $startDay); } $periodeMonth = strtolower($periodeDate->format('F')); $periodeYear = (int) $periodeDate->format('Y'); + // MENGAMBIL NILAI AKUMULASI CHECKLIST DARI JAVASCRIPT JS (Fallback ke total awal jika kosong) + $approvedTotal = $request->input('approved_total', $form->total); + // Ambil sisa budget sesuai periode $availableBudget = BudgetHelper::getAvailableBudget($cabang_id, $periodeMonth, $periodeYear); - if($form->approved_total > $availableBudget) { + if($approvedTotal > $availableBudget) { // <--- DISINKRONKAN DENGAN NILAI BARU HASIL CHECKLIST session()->flash('error', 'Budget cabang untuk periode ini tidak mencukupi, silahkan ajukan pada periode expense berikutnya'); return redirect()->back(); } + // UPDATE DATABASE SECARA AKURAT BERDASARKAN HASIL PILIHAN CHECKLIST FINANCE $form->update([ + 'approved_total' => $approvedTotal, // <--- SUNTIK NOMINAL BARU HASIL CHECKLIST KE DATABASE 'final_approved_at' => now(), 'final_approved_by' => auth()->user()->id, - 'status' => 'Closed', + 'status' => 'Closed', ]); $name = $form->user->name; $expense_number = $form->expense_number; $tanggal = $form->tanggal; - $total = $form->approved_total; + $total = $form->approved_total; // <--- OTOMATIS MEMAKAI DATA UPDATE TERBARU $roles = ['Head of Sales Marketing', 'Marketing Operational Manager Region']; $recipients = [$form->user->email, auth()->user()->email]; @@ -927,11 +972,11 @@ class FormUpCountryController extends Controller foreach ($roles as $role) { $users = Admin::getUserByRoleName($role)->filter(function ($user) use ($role, $cabang_id, $region_id) { if ($role === 'Marketing Information System' || $role === 'Head of Sales Marketing') { - return true; // Tidak ada filter cabang untuk peran ini + return true; } if (!$user->cabang || !$user->cabang->cabang) { - return false; // Pastikan user memiliki cabang + return false; } if ($role === 'Admin Region' || $role === 'Marketing Operational Manager Region') { @@ -985,16 +1030,21 @@ class FormUpCountryController extends Controller public function reject(Request $request, $id) { - $this->checkAuthorization(auth()->user(), ['approval.reject']); + if (!auth()->user()->hasAnyPermission(['approval.reject', 'approval2.reject', 'final_approval.approve'])) { + abort(403, 'Akses Ditolak: Anda tidak memiliki izin untuk melakukan Reject.'); + } $request->validate([ - 'remarks' => 'required|string|max:500', + 'remarks' => 'required|string|max:500', ]); $form = FormUpCountry::findOrfail($id); + $form->update([ - 'status' => 'Rejected', - 'remarks' => $request->remarks, + 'status' => 'Rejected', + 'remarks' => $request->remarks, + 'rejected_by' => auth()->id(), // SUNTIK DATA AUDIT TRAIL + 'rejected_at' => now(), // SUNTIK DATA AUDIT TRAIL ]); $heads = $form->user->getCabangAndRegionHead(); @@ -1003,53 +1053,37 @@ class FormUpCountryController extends Controller $tanggal = $form->tanggal; $total = $form->total; - // Collect all recipients $recipients = [$form->user->email, auth()->user()->email]; $phoneNumbers = [$form->user->phone, auth()->user()->phone]; if ($heads['cabang_head']) { - $recipients[] = $heads['cabang_head']['email']; - $phoneNumbers[] = $heads['cabang_head']['phone']; + $recipients[] = $heads['cabang_head']['email']; + $phoneNumbers[] = $heads['cabang_head']['phone']; } if ($heads['region_head']) { - $recipients[] = $heads['region_head']['email']; - $phoneNumbers[] = $heads['region_head']['phone']; + $recipients[] = $heads['region_head']['email']; + $phoneNumbers[] = $heads['region_head']['phone']; } - // Remove duplicate email addresses, if any $recipients = array_unique($recipients); $phoneNumbers = array_unique($phoneNumbers); - // send whatsapp message - $url = route('forms.up-country.view', $form->id); + $url = route('forms.up-country.view', $form->id); WhatsappHelper::rejectExpense($phoneNumbers, $expense_number, $url, $tanggal, $total, $name, $request->remarks); - // Send bulk email $brevoService = app(BrevoService::class); foreach ($recipients as $recipient) { - try { - $mail = new ExpenseRejected( - $name, - $expense_number, - $tanggal, - $total, - $url, - $request->remarks - ); - - $response = $brevoService->expenseRejected($recipient, $name, $mail); - - if (isset($response['success']) && $response['success'] === false) { - Log::warning("Email to {$recipient} failed: " . ($response['message'] ?? 'Unknown error')); + try { + $mail = new ExpenseRejected($name, $expense_number, $tanggal, $total, $url, $request->remarks); + $brevoService->expenseRejected($recipient, $name, $mail); + } catch (\Exception $e) { + Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); + continue; } - } catch (\Exception $e) { - Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); - continue; - } } - AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Up Country (' . $form->expense_number . ')'); + AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Up Country (' . $form->expense_number . ') oleh ' . auth()->user()->name); session()->flash('success', 'Form has been rejected.'); return redirect()->back(); } @@ -1070,12 +1104,12 @@ class FormUpCountryController extends Controller return redirect()->back(); } - public function destroy($id) - { + public function destroy($id) + { $this->checkAuthorization(auth()->user(), ['forms.country.delete']); $role = auth()->user()->getRoleNames()[0]; - $form = FormUpCountry::findOrfail($id); + $form = FormUpCountry::findOrfail($id); if($form->status != 'On Progress' && $role == 'Medical Representatif') { session()->flash('error', 'You cannot edit this form because it has been approved or rejected.'); return redirect()->back(); @@ -1083,9 +1117,9 @@ class FormUpCountryController extends Controller AuditTrailHelper::AddAuditTrail('Delete', 'Delete Record at Form Up Country (' . $form->expense_number . ')'); - $form->delete(); + $form->delete(); - session()->flash('success', 'Form has been deleted.'); - return redirect()->back(); - } -} + session()->flash('success', 'Form has been deleted.'); + return redirect()->back(); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Forms/FormVehicleController.php b/app/Http/Controllers/Forms/FormVehicleController.php index f5ca8b6..8062acb 100644 --- a/app/Http/Controllers/Forms/FormVehicleController.php +++ b/app/Http/Controllers/Forms/FormVehicleController.php @@ -68,7 +68,7 @@ class FormVehicleController extends Controller $role = auth()->user()->getRoleNames()[0]; $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $closingDay = (int) env('CLOSING_DATE', 13); // Referensi waktu sekarang $now = Carbon::now(); @@ -173,17 +173,17 @@ class FormVehicleController extends Controller ]); } - public function create() - { + public function create() + { $this->checkAuthorization(auth()->user(), ['forms.vehicle.create']); - return view('backend.pages.forms.vehicle.create', [ + return view('backend.pages.forms.vehicle.create', [ 'attachmentCategories' => $this->attachmentCategories, ]); - } + } - public function store(Request $request) - { + public function store(Request $request) + { $this->checkAuthorization(auth()->user(), ['forms.vehicle.create']); $validator = Validator::make($request->all(), [ 'tanggal' => 'required', @@ -226,23 +226,27 @@ class FormVehicleController extends Controller $validator->validate(); $tanggal = Carbon::parse($request->tanggal); + + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE $startDay = (int) env('STARTING_DATE', 11); - $closingDay = (int) env('CLOSING_DATE', 10); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); $now = Carbon::now(); - // Tentukan periode input berdasarkan waktu saat ini + // Tentukan periode aktif berdasarkan tanggal saat ini if ($now->day >= $startDay) { - $startDate = Carbon::create($now->year, $now->month, $startDay); - $closingDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $closingDay); + // Jendela Aktif: Tgl 11 Bulan Ini s/d Tgl 10 Bulan Depan + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); } else { - $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay); - $closingDate = Carbon::create($now->year, $now->month, $closingDay); + // Jendela Aktif: Tgl 11 Bulan Lalu s/d Tgl 10 Bulan Ini + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); } - // Validasi apakah tanggal form berada dalam periode input - if ($tanggal->lt($startDate) || $tanggal->gt($closingDate)) { - session()->flash('error', "Gagal, tanggal tidak berada dalam periode input: {$startDate->format('d M')} - {$closingDate->format('d M')}"); - return redirect()->back(); + // Validasi apakah tanggal pengajuan masuk ke periode input saat ini + if ($tanggal->lt($startDate) || $tanggal->gt($maxInputDate)) { + session()->flash('error', "Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: {$startDate->format('d M Y')} - {$maxInputDate->format('d M Y')}"); + return redirect()->back()->withInput(); } // Ambil cabang user @@ -252,7 +256,7 @@ class FormVehicleController extends Controller return redirect()->back(); } - // Tentukan periode budget berdasarkan tanggal input (bukan now) + // Tentukan periode budget dari tanggal input if ($tanggal->day >= $startDay) { $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); } else { @@ -266,18 +270,19 @@ class FormVehicleController extends Controller $availableBudget = BudgetHelper::getAvailableBudget($cabang->id, $periodeMonth, $periodeYear); if($request->total > $availableBudget) { session()->flash('error', 'Alokasi budget bulanan cabang tidak mencukupi,silahkan ajukan pada periode expense berikutnya'); - return redirect()->back(); + return redirect()->back()->withInput(); } $region = Region::where('id', $cabang->region_id)->first(); - $kategori = Kategori::where('name', 'Vehicle Running Cost')->first(); + // REPARASI SUNTIK KATEGORI: Penggunaan LIKE untuk toleransi kecocokan spasi + $kategori = Kategori::where('name', 'LIKE', '%Vehicle Running Cost%')->first(); $folderPath = '/expense/' . $region->code . '/' . $cabang->code . '/vehicle'; $attachmentsPayload = $this->buildAttachmentPayload($request, $folderPath); // Generate sequence number and expense number - $sequence_number = FormVehicleRunningCost::withTrashed()->where('expense_number', 'like', $region->code . '-' . $cabang->code . '-VHC-' . date('ym') . '%')->count() + 1; + $sequence_number = FormVehicleRunningCost::withTrashed()->where('expense_number', 'like', $region->code . '-' . $cabang->code . '-VHC-' . date('ym') . '%')->count() + 1; $formatted_sequence_number = str_pad($sequence_number, 4, '0', STR_PAD_LEFT); $expense_number = $region->code . '-' . $cabang->code . '-VHC-' . date('ym') . $formatted_sequence_number; @@ -286,6 +291,8 @@ class FormVehicleController extends Controller $form = FormVehicleRunningCost::create([ 'expense_number' => $expense_number, 'user_id' => auth()->user()->id, + 'cabang_id' => $cabang->id, // <--- REPARASI SUNTIK CABANG ID + 'kategori_id' => $kategori->id ?? null, // <--- REPARASI SUNTIK KATEGORI ID 'tanggal' => $request->tanggal, 'type' => $request->type, 'liter' => $request->liter ?? 0, @@ -295,7 +302,7 @@ class FormVehicleController extends Controller 'nopol' => $request->nopol ?? '-', 'keterangan' => $request->keterangan, 'bukti_total' => null, - 'account_number' => $kategori->account_number, + 'account_number' => $kategori->account_number ?? null, 'status' => 'On Progress', ]); @@ -399,10 +406,10 @@ class FormVehicleController extends Controller AuditTrailHelper::AddAuditTrail('Insert', 'Insert New Expense at Form Vehicle Running Cost (' . $expense_number . ')'); session()->flash('success', 'Form has been created.'); return redirect()->back(); - } + } - public function edit($id) - { + public function edit($id) + { $this->checkAuthorization(auth()->user(), ['forms.vehicle.edit']); $role = auth()->user()->getRoleNames()[0]; @@ -412,30 +419,48 @@ class FormVehicleController extends Controller return redirect()->back(); } - return view('backend.pages.forms.vehicle.edit', [ + return view('backend.pages.forms.vehicle.edit', [ 'form' => $form, 'attachments' => $this->formatAttachmentCollection($form), 'attachmentCategories' => $this->attachmentCategories, - ]); - } + ]); + } - public function update(Request $request, $id) - { + public function update(Request $request, $id) + { $this->checkAuthorization(auth()->user(), ['forms.vehicle.edit']); $role = auth()->user()->getRoleNames()[0]; $form = FormVehicleRunningCost::with('attachments')->findOrFail($id); // Ambil cabang ID dari user yang mengisi form - $cabang_id = UserHasCabang::where('user_id', $form->user_id)->first()?->cabang_id; - if (!$cabang_id) { + $cabang = UserHasCabang::with('cabang')->where('user_id', $form->user_id)->first()?->cabang; + if (!$cabang) { session()->flash('error', 'Data cabang tidak ditemukan.'); return redirect()->back(); } - // Hitung periode berdasarkan tanggal input $tanggal = Carbon::parse($request->tanggal); + + // PEMBATASAN INPUT MENGGUNAKAN STARTING_DATE & INPUT_MAX_DATE $startDay = (int) env('STARTING_DATE', 11); + $inputMaxDay = (int) env('INPUT_MAX_DATE', 10); + $now = Carbon::now(); + if ($now->day >= $startDay) { + $startDate = Carbon::create($now->year, $now->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->copy()->addMonth()->year, $now->copy()->addMonth()->month, $inputMaxDay)->endOfDay(); + } else { + $startDate = Carbon::create($now->copy()->subMonth()->year, $now->copy()->subMonth()->month, $startDay)->startOfDay(); + $maxInputDate = Carbon::create($now->year, $now->month, $inputMaxDay)->endOfDay(); + } + + // Validasi apakah tanggal pengajuan masuk ke periode input saat ini + if ($tanggal->lt($startDate) || $tanggal->gt($maxInputDate)) { + session()->flash('error', "Gagal, tanggal expense tidak berada dalam batas waktu input yang diizinkan: {$startDate->format('d M Y')} - {$maxInputDate->format('d M Y')}"); + return redirect()->back()->withInput(); + } + + // Hitung periode dari tanggal form yang diinput if ($tanggal->day >= $startDay) { $periodeDate = Carbon::create($tanggal->year, $tanggal->month, $startDay); } else { @@ -446,7 +471,7 @@ class FormVehicleController extends Controller $periodeYear = (int) $periodeDate->format('Y'); // Hitung sisa budget - $availableBudget = BudgetHelper::getAvailableBudget($cabang_id, $periodeMonth, $periodeYear); + $availableBudget = BudgetHelper::getAvailableBudget($cabang->id, $periodeMonth, $periodeYear); // Validasi input $validator = Validator::make($request->all(), [ @@ -494,9 +519,9 @@ class FormVehicleController extends Controller return redirect()->back(); } - $cabang = UserHasCabang::with('cabang')->where('user_id', $form->user_id)->first()->cabang; $region = Region::where('id', $cabang->region_id)->first(); - $kategori = Kategori::where('name', 'Vehicle Running Cost')->first(); + // REPARASI SUNTIK KATEGORI_ID SAAT UPDATE + $kategori = Kategori::where('name', 'LIKE', '%Vehicle Running Cost%')->first(); $folderPath = '/expense/' . $region->code . '/' . $cabang->code . '/vehicle'; @@ -505,6 +530,8 @@ class FormVehicleController extends Controller DB::beginTransaction(); try { $form->update([ + 'cabang_id' => $cabang->id, // <--- REPARASI SUNTIK CABANG_ID + 'kategori_id' => $kategori->id ?? null, // <--- REPARASI SUNTIK KATEGORI_ID 'tanggal' => $request->tanggal, 'type' => $request->type, 'liter' => $request->liter ?? 0, @@ -514,7 +541,7 @@ class FormVehicleController extends Controller 'nopol' => $request->nopol ?? '-', 'keterangan' => $request->keterangan, 'bukti_total' => $form->bukti_total, - 'account_number' => $kategori->account_number, + 'account_number' => $kategori->account_number ?? $form->account_number, 'status' => 'On Progress', ]); @@ -542,7 +569,7 @@ class FormVehicleController extends Controller AuditTrailHelper::AddAuditTrail('Update', 'Update Record at Form Vehicle Running Cost (' . $form->expense_number . ')'); session()->flash('success', 'Form has been updated.'); return redirect()->back(); - } + } public function deleteAttachment(Request $request, $formId, $attachmentId) { @@ -755,7 +782,8 @@ class FormVehicleController extends Controller return redirect()->back(); } - public function finalApprove($id) + // <--- REPARASI SUNTIKAN REQUEST DARI JAVASCRIPT MODAL AGAR DATA BISA DIAMBIL + public function finalApprove(Request $request, $id) { $this->checkAuthorization(auth()->user(), ['final_approval.approve']); $form = FormVehicleRunningCost::findOrFail($id); @@ -785,23 +813,27 @@ class FormVehicleController extends Controller $periodeMonth = strtolower($periodeDate->format('F')); $periodeYear = (int) $periodeDate->format('Y'); - // Hitung sisa budget cabang di periode tersebut + // <--- REPARASI: Penangkapan nilai payload dari checkbox JavaScript + $approvedTotal = $request->input('approved_total', $form->total); + + // Hitung sisa budget cabang di periode tersebut disinkronisasi dengan variabel kalkulasi terbaru $availableBudget = BudgetHelper::getAvailableBudget($cabang_id, $periodeMonth, $periodeYear); - if($form->approved_total > $availableBudget) { + if($approvedTotal > $availableBudget) { session()->flash('error', 'Budget cabang untuk periode ini tidak mencukupi, silahkan ajukan pada periode expense berikutnya'); return redirect()->back(); } $form->update([ + 'approved_total' => $approvedTotal, // <--- SUNTIK DATA UPDATE HASIL CHECKLIST 'final_approved_at' => now(), 'final_approved_by' => auth()->user()->id, - 'status' => 'Closed', + 'status' => 'Closed', ]); $name = $form->user->name; $expense_number = $form->expense_number; $tanggal = $form->created_at; - $total = $form->approved_total; + $total = $form->approved_total; // <--- TOTAL BARU YANG DIKIRIMKAN KE EMAIL / WHATSAPP $roles = ['Head of Sales Marketing', 'Marketing Operational Manager Region']; $recipients = [$form->user->email, auth()->user()->email]; @@ -874,75 +906,64 @@ class FormVehicleController extends Controller } public function reject(Request $request, $id) - { - $this->checkAuthorization(auth()->user(), ['approval.reject']); +{ + if (!auth()->user()->hasAnyPermission(['approval.reject', 'approval2.reject', 'final_approval.approve'])) { + abort(403, 'Akses Ditolak: Anda tidak memiliki izin untuk melakukan Reject.'); + } $request->validate([ - 'remarks' => 'required|string|max:500', + 'remarks' => 'required|string|max:500', ]); $form = FormVehicleRunningCost::findOrfail($id); + $form->update([ - 'status' => 'Rejected', - 'remarks' => $request->remarks, - ]); + 'status' => 'Rejected', + 'remarks' => $request->remarks, + 'rejected_by' => auth()->id(), // Kolom baru + 'rejected_at' => now(), // Kolom baru +]); $heads = $form->user->getCabangAndRegionHead(); $name = $form->user->name; $expense_number = $form->expense_number; - $tanggal = $form->created_at; + $tanggal = $form->tanggal; $total = $form->total; - // Collect all recipients $recipients = [$form->user->email, auth()->user()->email]; $phoneNumbers = [$form->user->phone, auth()->user()->phone]; if ($heads['cabang_head']) { - $recipients[] = $heads['cabang_head']['email']; - $phoneNumbers[] = $heads['cabang_head']['phone']; + $recipients[] = $heads['cabang_head']['email']; + $phoneNumbers[] = $heads['cabang_head']['phone']; } if ($heads['region_head']) { - $recipients[] = $heads['region_head']['email']; - $phoneNumbers[] = $heads['region_head']['phone']; + $recipients[] = $heads['region_head']['email']; + $phoneNumbers[] = $heads['region_head']['phone']; } - // Remove duplicate email addresses, if any $recipients = array_unique($recipients); $phoneNumbers = array_unique($phoneNumbers); - // send whatsapp message $url = route('forms.vehicle.view', $form->id); WhatsappHelper::rejectExpense($phoneNumbers, $expense_number, $url, $tanggal, $total, $name, $request->remarks); - // Send bulk email $brevoService = app(BrevoService::class); foreach ($recipients as $recipient) { - try { - $mail = new ExpenseRejected( - $name, - $expense_number, - $tanggal, - $total, - $url, - $request->remarks - ); - - $response = $brevoService->expenseRejected($recipient, $name, $mail); - - if (isset($response['success']) && $response['success'] === false) { - Log::warning("Email to {$recipient} failed: " . ($response['message'] ?? 'Unknown error')); + try { + $mail = new ExpenseRejected($name, $expense_number, $tanggal, $total, $url, $request->remarks); + $brevoService->expenseRejected($recipient, $name, $mail); + } catch (\Exception $e) { + Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); + continue; } - } catch (\Exception $e) { - Log::error("Failed to send email to {$recipient}: " . $e->getMessage()); - continue; - } } - AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Vehicle Running Cost (' . $form->expense_number . ')'); + AuditTrailHelper::AddAuditTrail('Reject', 'Reject Expense at Form Vehicle Running Cost (' . $form->expense_number . ') oleh ' . auth()->user()->name); session()->flash('success', 'Form has been rejected.'); return redirect()->back(); - } +} public function open($id) { @@ -960,12 +981,12 @@ class FormVehicleController extends Controller return redirect()->back(); } - public function destroy($id) - { + public function destroy($id) + { $this->checkAuthorization(auth()->user(), ['forms.vehicle.delete']); $role = auth()->user()->getRoleNames()[0]; - $form = FormVehicleRunningCost::findOrfail($id); + $form = FormVehicleRunningCost::findOrfail($id); if($form->status != 'On Progress' && $role == 'Medical Representatif') { session()->flash('error', 'You cannot edit this form because it has been approved or rejected.'); return redirect()->back(); @@ -973,11 +994,11 @@ class FormVehicleController extends Controller AuditTrailHelper::AddAuditTrail('Delete', 'Delete Record at Form Vehicle Running Cost (' . $form->expense_number . ')'); - $form->delete(); + $form->delete(); - session()->flash('success', 'Form has been deleted.'); - return redirect()->back(); - } + session()->flash('success', 'Form has been deleted.'); + return redirect()->back(); + } protected function formatAttachmentCollection(FormVehicleRunningCost $form): array { @@ -1080,4 +1101,4 @@ class FormVehicleController extends Controller return $payload; } -} +} \ No newline at end of file diff --git a/app/Models/BudgetControl.php b/app/Models/BudgetControl.php index 06e7eec..be95e6f 100644 --- a/app/Models/BudgetControl.php +++ b/app/Models/BudgetControl.php @@ -21,6 +21,8 @@ class BudgetControl extends Model 'transferred_at', 'closing_at', 'status', + 'transfer_proof', + 'transfer_status', ]; public function user() diff --git a/app/Models/FormEntertaimentPresentation.php b/app/Models/FormEntertaimentPresentation.php index 492ee0f..292c43a 100644 --- a/app/Models/FormEntertaimentPresentation.php +++ b/app/Models/FormEntertaimentPresentation.php @@ -7,7 +7,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; class FormEntertaimentPresentation extends Model { - use SoftDeletes; + use SoftDeletes, \App\Traits\HasAttachments; protected $table = 'form_entertainment_presentation'; protected $fillable = [ @@ -31,17 +31,40 @@ class FormEntertaimentPresentation extends Model 'approved2_by', 'final_approved_at', 'final_approved_by', + // Kolom baru + 'jabatan', + 'nama_perusahaan', + 'jenis_usaha', ]; + protected $casts = [ + 'created_at' => 'datetime', + 'approved_at' => 'datetime', + 'approved2_at' => 'datetime', + 'final_approved_at' => 'datetime', +]; + public function user() { return $this->belongsTo('App\Models\Admin'); } - public function attachments() - { - return $this->hasMany(\App\Models\AttachmentForm::class, 'form_id') - ->where('table_name', \App\Enums\AttachmentTableName::FORM_ENTERTAINMENT_PRESENTATION); - } + // Relasi ke kategori untuk mengambil account_number +public function kategori() +{ + return $this->belongsTo(Kategori::class, 'kategori_id'); +} + +public function rejector() { + return $this->belongsTo(User::class, 'rejected_by'); +} + +// public function attachments() +// { +// // form_id adalah kolom di tabel attachment_forms +// // table_name digunakan jika satu tabel attachment dipakai banyak form +// return $this->hasMany(\App\Models\AttachmentForm::class, 'form_id') +// ->where('table_name', 'form_entertainment_presentation'); +// } } diff --git a/app/Models/FormMeetingSeminar.php b/app/Models/FormMeetingSeminar.php index 5dd5f94..1099c1a 100644 --- a/app/Models/FormMeetingSeminar.php +++ b/app/Models/FormMeetingSeminar.php @@ -4,46 +4,53 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; class FormMeetingSeminar extends Model { use SoftDeletes; protected $table = 'form_meeting_seminar'; + protected $fillable = [ - 'expense_number', - 'user_id', - 'tanggal', - 'allowance', - 'transport_ankot', - 'hotel', - 'total', - 'approved_allowance', - 'approved_transport_ankot', - 'approved_hotel', - 'approved_total', - 'bukti_allowance', - 'bukti_transport_ankot', - 'bukti_hotel', - 'account_number', - 'status', - 'remarks', - 'approved_at', - 'approved2_at', - 'approved_by', - 'approved2_by', - 'final_approved_at', + 'expense_number', 'user_id', 'tanggal', 'allowance', 'transport_ankot', + 'hotel', 'total', 'approved_allowance', 'approved_transport_ankot', + 'approved_hotel', 'approved_total', 'bukti_allowance', 'bukti_transport_ankot', + 'bukti_hotel', 'account_number', 'status', 'remarks', 'approved_at', + 'approved2_at', 'approved_by', 'approved2_by', 'final_approved_at', 'final_approved_by', ]; - public function user() + /** + * Casts untuk otomatisasi format Carbon pada tanggal agar konsisten di Blade. + */ + protected $casts = [ + 'tanggal' => 'date', + 'final_approved_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Relasi ke Admin (Pengaju). + */ + public function user(): BelongsTo { return $this->belongsTo(Admin::class, 'user_id'); } - public function attachments() + /** + * Relasi ke Attachments (Dokumen Digital) sesuai pola Entertainment. + */ + public function attachments(): HasMany { - return $this->hasMany(\App\Models\AttachmentForm::class, 'form_id') + return $this->hasMany(AttachmentForm::class, 'form_id') ->where('table_name', \App\Enums\AttachmentTableName::FORM_MEETING_SEMINAR); } + +public function rejector() { + return $this->belongsTo(User::class, 'rejected_by'); } + +} \ No newline at end of file diff --git a/app/Models/FormOthers.php b/app/Models/FormOthers.php index c6f5e1b..9ffc5d2 100644 --- a/app/Models/FormOthers.php +++ b/app/Models/FormOthers.php @@ -4,6 +4,8 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; class FormOthers extends Model { @@ -11,38 +13,49 @@ class FormOthers extends Model protected $table = 'form_others'; protected $fillable = [ - 'expense_number', - 'user_id', - 'kategori_id', - 'tanggal', - 'keterangan', - 'total', - 'approved_total', - 'bukti_total', - 'account_number', - 'status', - 'remarks', - 'approved_at', - 'approved2_at', - 'approved_by', - 'approved2_by', - 'final_approved_at', - 'final_approved_by', + 'expense_number', 'user_id', 'kategori_id', 'tanggal', 'keterangan', + 'total', 'approved_total', 'bukti_total', 'account_number', 'status', + 'remarks', 'approved_at', 'approved2_at', 'approved_by', 'approved2_by', + 'final_approved_at', 'final_approved_by', ]; - public function user() + /** + * Casts untuk otomatisasi format Carbon pada tanggal agar konsisten di Blade. + */ + protected $casts = [ + 'tanggal' => 'date', + 'final_approved_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Relasi ke Admin (Pengaju). + */ + public function user(): BelongsTo { - return $this->belongsTo(Admin::class); + return $this->belongsTo(Admin::class, 'user_id'); } - public function kategori() + /** + * Relasi ke Kategori. + */ + public function kategori(): BelongsTo { - return $this->belongsTo(Kategori::class); + return $this->belongsTo(Kategori::class, 'kategori_id'); } - public function attachments() + /** + * Relasi ke Attachments (Dokumen Digital) sesuai pola Entertainment. + */ + public function attachments(): HasMany { return $this->hasMany(\App\Models\AttachmentForm::class, 'form_id') ->where('table_name', \App\Enums\AttachmentTableName::FORM_OTHERS); } + +public function rejector() { + return $this->belongsTo(User::class, 'rejected_by'); } + +} \ No newline at end of file diff --git a/app/Models/FormUpCountry.php b/app/Models/FormUpCountry.php index e20c3c5..5a2e89a 100644 --- a/app/Models/FormUpCountry.php +++ b/app/Models/FormUpCountry.php @@ -4,58 +4,61 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\BelongsTo; class FormUpCountry extends Model { use SoftDeletes; protected $table = 'form_up_country'; + protected $fillable = [ - 'expense_number', - 'user_id', - 'rayon_id', - 'tanggal', - 'tujuan', - 'jarak', - 'allowance', - 'transport_dalkot', - 'transport_ankot', - 'hotel', - 'total', - 'approved_allowance', - 'approved_transport_dalkot', - 'approved_transport_ankot', - 'approved_hotel', - 'approved_total', - 'bukti_allowance', - 'bukti_transport_dalkot', - 'bukti_transport_ankot', - 'bukti_hotel', - 'uc_plan', - 'account_number', - 'status', - 'remarks', - 'approved_at', - 'approved2_at', - 'approved_by', - 'approved2_by', - 'final_approved_at', - 'final_approved_by', + 'expense_number', 'user_id', 'rayon_id', 'tanggal', 'tujuan', 'jarak', + 'allowance', 'transport_dalkot', 'transport_ankot', 'hotel', 'total', + 'approved_allowance', 'approved_transport_dalkot', 'approved_transport_ankot', + 'approved_hotel', 'approved_total', 'bukti_allowance', 'bukti_transport_dalkot', + 'bukti_transport_ankot', 'bukti_hotel', 'uc_plan', 'account_number', 'status', + 'remarks', 'approved_at', 'approved2_at', 'approved_by', 'approved2_by', + 'final_approved_at', 'final_approved_by','cabang_id','kategori_id', ]; - public function user() + /** + * Casts untuk otomatisasi format Carbon pada tanggal. + */ + protected $casts = [ + 'tanggal' => 'date', + 'final_approved_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Relasi ke Admin (Pengaju). + */ + public function user(): BelongsTo { - return $this->belongsTo('App\Models\Admin'); + return $this->belongsTo(Admin::class, 'user_id'); } - public function rayon() + /** + * Relasi ke Rayon. + */ + public function rayon(): BelongsTo { - return $this->belongsTo('App\Models\Rayon'); + return $this->belongsTo(Rayon::class, 'rayon_id'); } - public function attachments() + /** + * Relasi ke Attachments (Dokumen Digital) sesuai pola Entertainment. + */ + public function attachments(): HasMany { - return $this->hasMany(\App\Models\AttachmentForm::class, 'form_id') + return $this->hasMany(AttachmentForm::class, 'form_id') ->where('table_name', \App\Enums\AttachmentTableName::FORM_UP_COUNTRY); } + +public function rejector() { + return $this->belongsTo(User::class, 'rejected_by'); } +} \ No newline at end of file diff --git a/app/Models/FormVehicleRunningCost.php b/app/Models/FormVehicleRunningCost.php index a2e3708..1315c44 100644 --- a/app/Models/FormVehicleRunningCost.php +++ b/app/Models/FormVehicleRunningCost.php @@ -4,44 +4,51 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; class FormVehicleRunningCost extends Model { use SoftDeletes; protected $table = 'form_vehicle_running_cost'; + protected $fillable = [ - 'expense_number', - 'user_id', - 'tanggal', - 'liter', - 'total', - 'approved_total', - 'jarak', - 'tipe_bensin', - 'nopol', - 'keterangan', - 'bukti_total', - 'account_number', - 'type', - 'status', - 'remarks', - 'approved_at', - 'approved2_at', - 'approved_by', - 'approved2_by', - 'final_approved_at', - 'final_approved_by', + 'expense_number', 'user_id', 'tanggal', 'liter', 'total', 'approved_total', + 'jarak', 'tipe_bensin', 'nopol', 'keterangan', 'bukti_total', 'account_number', + 'type', 'status', 'remarks', 'approved_at', 'approved2_at', 'approved_by', + 'approved2_by', 'final_approved_at', 'final_approved_by', ]; - public function user() + /** + * Casts untuk otomatisasi format Carbon pada tanggal. + */ + protected $casts = [ + 'tanggal' => 'date', + 'final_approved_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * Relasi ke Admin (Pengaju) + */ + public function user(): BelongsTo { - return $this->belongsTo('App\Models\Admin', 'user_id'); + return $this->belongsTo(Admin::class, 'user_id'); } - public function attachments() + /** + * Relasi ke Attachments (Dokumen Digital) + */ + public function attachments(): HasMany { - return $this->hasMany(\App\Models\AttachmentForm::class, 'form_id') + return $this->hasMany(AttachmentForm::class, 'form_id') ->where('table_name', \App\Enums\AttachmentTableName::FORM_VEHICLE_RUNNING_COST); } + +public function rejector() { + return $this->belongsTo(User::class, 'rejected_by'); } + +} \ No newline at end of file diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php new file mode 100644 index 0000000..1a537b0 --- /dev/null +++ b/app/Models/Transaction.php @@ -0,0 +1,25 @@ + 'date', + 'amount' => 'decimal:2', + ]; + + public function cabang() { + return $this->belongsTo(Cabang::class); + } +} \ No newline at end of file diff --git a/app/Models/User.php b/app/Models/User.php index 8869a39..1273289 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -25,6 +25,8 @@ class User extends Authenticatable 'username', 'password', 'phone', + 'region_id', + 'cabang_id' ]; /** @@ -49,4 +51,42 @@ class User extends Authenticatable 'password' => 'hashed', ]; } + + /** + * Helper untuk mendapatkan ID Cabang & Region milik user + */ +public function getMyCabangAndRegionId(): array +{ + // Jika User punya cabang_id (Level AM / MR) + if ($this->cabang_id) { + $cabang = \DB::table('cabang')->where('id', $this->cabang_id)->first(); + return [ + 'cabang' => (int) $this->cabang_id, + 'region' => $cabang ? (int) $cabang->region_id : null + ]; + } + + // Jika User level Region (Admin Region / MOM Region) + // Pastikan kolom 'region_id' ada di tabel users Anda + return [ + 'cabang' => null, + 'region' => $this->region_id ? (int) $this->region_id : null + ]; +} + +public function cabangs() +{ + // Relasi Many-to-Many melalui tabel pivot user_has_cabang + return $this->belongsToMany(Cabang::class, 'user_has_cabang', 'user_id', 'cabang_id'); +} + +/** + * Mendapatkan ID Region berdasarkan penugasan cabang pertama + */ +public function getAssignedRegionId() +{ + $firstAssignment = $this->cabangs()->first(); + return $firstAssignment ? $firstAssignment->region_id : null; +} + } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 8437a54..830efb8 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -4,6 +4,7 @@ namespace App\Providers; use Illuminate\Support\Facades\URL; use Illuminate\Support\ServiceProvider; +use Illuminate\Pagination\Paginator; class AppServiceProvider extends ServiceProvider @@ -29,6 +30,8 @@ class AppServiceProvider extends ServiceProvider URL::forceScheme('https'); } + Paginator::useBootstrap(); + } } diff --git a/app/Services/DashboardService.php b/app/Services/DashboardService.php new file mode 100644 index 0000000..5c9414a --- /dev/null +++ b/app/Services/DashboardService.php @@ -0,0 +1,284 @@ +user = $user; + $this->setPeriode(); + $this->setUserVisibility(); + } + + /** + * Set Periode Cut-off (11 - 10) + */ + protected function setPeriode(): void + { + $startDay = (int) env('STARTING_DATE', 11); + $closingDay = (int) env('CLOSING_DATE', 10); + $now = Carbon::now(); + + if ($now->day >= $startDay) { + $this->startDate = $now->copy()->day($startDay)->startOfDay(); + $this->endDate = $now->copy()->addMonth()->day($closingDay)->endOfDay(); + } else { + $this->startDate = $now->copy()->subMonth()->day($startDay)->startOfDay(); + $this->endDate = $now->copy()->day($closingDay)->endOfDay(); + } + } + + /** + * Atur Visibilitas Berdasarkan Hierarki Role + */ + protected function setUserVisibility(): void + { + // 1. MIS / HEAD / SUPERADMIN: Akses Nasional + if ($this->user->hasRole(['Marketing Information System', 'Head of Sales Management', 'Super Admin'])) { + $this->allowedCabangIds = []; + return; + } + + $myCabangs = DB::table('user_has_cabang') + ->where('user_id', $this->user->id) + ->whereNull('deleted_at') + ->pluck('cabang_id') + ->toArray(); + + // 2. MOM / ADMIN REGION: Akses Regional (Semua cabang di dalam regionnya) + if ($this->user->hasRole(['Marketing Operational Manager Region', 'Admin Region'])) { + $regionIds = DB::table('cabang') + ->whereIn('id', $myCabangs) + ->pluck('region_id') + ->toArray(); + + $this->allowedCabangIds = DB::table('cabang') + ->whereIn('region_id', $regionIds) + ->pluck('id') + ->toArray(); + } + // 3. AM / MR: Akses Cabang Spesifik + else { + $this->allowedCabangIds = $myCabangs; + } + } + + /** + * Helper Filter: Digunakan di semua query analitik + */ + protected function applyFilters($query) + { + if (!empty($this->allowedCabangIds)) { + $query->whereIn('combined_forms.cabang_id', $this->allowedCabangIds); + } + return $query; + } + + /** + * Query Utama: Menggabungkan 5 tabel kategori expense + */ + protected function getCombinedFormsQuery() + { + $tables = [ + 'form_up_country', + 'form_vehicle_running_cost', + 'form_entertainment_presentation', + 'form_meeting_seminar', + 'form_others' + ]; + + $query = null; + foreach ($tables as $table) { + $sub = DB::table($table) + ->select( + 'id', + 'cabang_id', + 'total', + 'status', + 'created_at', + 'final_approved_at', + DB::raw("'$table' as source_table") + ) + ->whereNull('deleted_at'); + $query = ($query === null) ? $sub : $query->unionAll($sub); + } + + return DB::table(DB::raw("({$query->toSql()}) as combined_forms")) + ->mergeBindings($query) + ->join('cabang', 'combined_forms.cabang_id', '=', 'cabang.id') + ->join('region', 'cabang.region_id', '=', 'region.id'); + } + + /** + * Data untuk 4 Kartu Statistik Utama + */ + public function getApprovalStats(): array +{ + // Ambil semua data tanpa filter tanggal dulu untuk kartu "Waiting" + $allData = $this->getCombinedFormsQuery() + ->select('combined_forms.status', 'combined_forms.total', 'combined_forms.created_at') + ->get(); + + $map = [ + // 1. TOTAL PENDING: Khusus periode aktif (11 Maret - 10 April) & Status On Progress + 'total_pending' => [ + 'label' => 'Total Pending', + 'keys' => ['on progress'], + 'color' => 'warning', + 'icon' => 'fas fa-hourglass-half', + 'use_period' => true + ], + // 2. WAITING APPR 1: Semua yang statusnya On Progress + 'appr1' => [ + 'label' => 'Waiting Appr 1', + 'keys' => ['on progress'], + 'color' => 'info', + 'icon' => 'fas fa-user-check', + 'use_period' => false + ], + // 3. WAITING APPR 2: Semua yang statusnya Approved 1 + 'appr2' => [ + 'label' => 'Waiting Appr 2', + 'keys' => ['approved 1'], + 'color' => 'primary', + 'icon' => 'fas fa-file-invoice-dollar', + 'use_period' => false + ], + // 4. WAITING FINAL APPROVED: Semua yang statusnya Approved 2 + 'waiting_final' => [ + 'label' => 'Waiting Final Approved', + 'keys' => ['approved 2'], + 'color' => 'secondary', + 'icon' => 'fas fa-tasks', + 'use_period' => false + ], + // 5. FINAL APPROVED (CLOSED): Semua yang sudah Closed + 'closed' => [ + 'label' => 'Final Approved', + 'keys' => ['closed', 'approved'], + 'color' => 'success', + 'icon' => 'fas fa-check-double', + 'use_period' => false + ], + ]; + + $results = []; + foreach ($map as $config) { + $filtered = $allData->filter(function ($item) use ($config) { + $statusMatch = in_array(strtolower(trim((string)$item->status)), $config['keys']); + + // Jika kartu membutuhkan filter periode (Maret 11 - April 10) + if ($config['use_period']) { + $dateMatch = Carbon::parse($item->created_at)->between($this->startDate, $this->endDate); + return $statusMatch && $dateMatch; + } + + return $statusMatch; + }); + + $results[] = [ + 'label' => $config['label'], + 'count' => $filtered->count(), + 'amount' => (float) $filtered->sum('total'), + 'color' => $config['color'], + 'icon' => $config['icon'] + ]; + } + return $results; +} + + /** + * Mengambil daftar pengajuan dengan status 'Approved 2' + * Untuk antrian persetujuan akhir (HOSM/MIS) + */ + public function getPendingFinalApproval() + { + $query = $this->getCombinedFormsQuery() + ->select( + 'combined_forms.*', + 'cabang.name as cabang_name', + 'region.name as region_name' + ) + ->where('combined_forms.status', 'Approved 2'); + + $this->applyFilters($query); + + return $query->orderBy('combined_forms.created_at', 'desc')->paginate(10); + } + + /** + * Summary per Kategori untuk Chart + */ + public function getTotals(): array + { + $query = $this->getCombinedFormsQuery() + ->whereBetween('combined_forms.created_at', [$this->startDate, $this->endDate]) + ->whereIn('combined_forms.status', ['Approved', 'Closed']); + + $this->applyFilters($query); + + $results = $query->select('combined_forms.source_table', DB::raw('SUM(combined_forms.total) as total_per_table')) + ->groupBy('combined_forms.source_table') + ->get(); + + return [ + 'forms' => [ + 'upcountry' => $results->where('source_table', 'form_up_country')->first()->total_per_table ?? 0, + 'vehicle' => $results->where('source_table', 'form_vehicle_running_cost')->first()->total_per_table ?? 0, + 'entertainment' => $results->where('source_table', 'form_entertainment_presentation')->first()->total_per_table ?? 0, + 'meeting' => $results->where('source_table', 'form_meeting_seminar')->first()->total_per_table ?? 0, + 'others' => $results->where('source_table', 'form_others')->first()->total_per_table ?? 0, + ], + 'grand_total' => $results->sum('total_per_table') + ]; + } + + /** + * Top 5 Pengeluaran Terbanyak per Cabang + */ + /** + * Top 10 Pengeluaran Terbanyak (Volume Giants) + */ +public function getTopExpenses() +{ + $query = $this->getCombinedFormsQuery() + ->whereBetween('combined_forms.created_at', [$this->startDate, $this->endDate]); + + $this->applyFilters($query); + + return $query->select('cabang.name', 'region.name as region_name', DB::raw('SUM(combined_forms.total) as total_amount')) + ->groupBy('cabang.id', 'cabang.name', 'region.name') + ->orderBy('total_amount', 'desc') + ->take(10) // Update ke 10 + ->get(); +} + +/** + * Top 10 Efisiensi Approval (Speed Kings) + */ +public function getFastestClosing() +{ + $query = $this->getCombinedFormsQuery() + ->select('cabang.name', DB::raw('AVG(TIMESTAMPDIFF(HOUR, combined_forms.created_at, combined_forms.final_approved_at)) as avg_hours')) + ->whereIn('combined_forms.status', ['Approved', 'Closed']) + ->whereNotNull('combined_forms.final_approved_at') + ->whereBetween('combined_forms.final_approved_at', [$this->startDate, $this->endDate]); + + $this->applyFilters($query); + + return $query->groupBy('cabang.id', 'cabang.name') + ->orderBy('avg_hours', 'asc') + ->take(10) // Update ke 10 + ->get(); +} + +} \ No newline at end of file diff --git a/app/Traits/HasAttachments.php b/app/Traits/HasAttachments.php new file mode 100644 index 0000000..2d353dc --- /dev/null +++ b/app/Traits/HasAttachments.php @@ -0,0 +1,15 @@ +hasMany(AttachmentForm::class, 'form_id') + ->where('table_name', $this->table); + } +} \ No newline at end of file diff --git a/as $budget) { b/as $budget) { new file mode 100644 index 0000000..d830684 --- /dev/null +++ b/as $budget) { @@ -0,0 +1,484 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ESC-u Undo (toggle) search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines + --------------------------------------------------- + A search pattern may be preceded by one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_> + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-M_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c -C .... --clear-screen --CLEAR-SCREEN + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D [_x_n_._n] . --color=_x_n_._n + Set screen colors. (MS-DOS only) + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + --old-bot + Revert to the old bottom of screen behavior. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] + Use a lesskey file. + -K --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n ........ --line-numbers + Don't use line numbers. + -N ........ --LINE-NUMBERS + Use line numbers. + -o [_f_i_l_e] . --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t [_t_a_g] .. --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Horizontal scroll amount (0 = one half screen width) + ........ --no-keypad + Don't send termcap keypad init/deinit strings. + ........ --follow-name + The F command changes files if the input file is renamed. + ........ --use-backslash + Subsequent options use backslash as escape char. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. + + + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ESC-u Undo (toggle) search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines + --------------------------------------------------- + A search pattern may be preceded by one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_> + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-M_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c -C .... --clear-screen --CLEAR-SCREEN + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D [_x_n_._n] . --color=_x_n_._n + Set screen colors. (MS-DOS only) + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + --old-bot + Revert to the old bottom of screen behavior. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] + Use a lesskey file. + -K --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n ........ --line-numbers + Don't use line numbers. + -N ........ --LINE-NUMBERS + Use line numbers. + -o [_f_i_l_e] . --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t [_t_a_g] .. --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Horizontal scroll amount (0 = one half screen width) + ........ --no-keypad + Don't send termcap keypad init/deinit strings. + ........ --follow-name + The F command changes files if the input file is renamed. + ........ --use-backslash + Subsequent options use backslash as escape char. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. + + diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore old mode 100644 new mode 100755 diff --git a/composer.json b/composer.json index 0551ea9..c6fb9b7 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,10 @@ "name": "laravel/laravel", "type": "project", "description": "The skeleton application for the Laravel framework.", - "keywords": ["laravel", "framework"], + "keywords": [ + "laravel", + "framework" + ], "license": "MIT", "require": { "php": "^8.2", @@ -16,8 +19,8 @@ "laravel/ui": "^4.6", "laravolt/avatar": "^6.0", "league/flysystem-webdav": "^3.29", + "maatwebsite/excel": "^3.1", "nino/laravel-nextcloud-fs": "^2.0", - "phpoffice/phpspreadsheet": "^3.5", "singlequote/laravel-webdav": "^1.1", "spatie/image": "^3.7", "spatie/laravel-html": "^3.11", diff --git a/composer.lock b/composer.lock index 2460a4c..5342fa8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,30 +4,30 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1ad355916564d86a9848db17b8afa298", + "content-hash": "b234d7d1078a138915f86ad95f921f72", "packages": [ { "name": "barryvdh/laravel-dompdf", - "version": "v3.0.0", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-dompdf.git", - "reference": "2d814cd25e299be022a4fa0fb4509d7ccd353d8a" + "reference": "ee3b72b19ccdf57d0243116ecb2b90261344dedc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/2d814cd25e299be022a4fa0fb4509d7ccd353d8a", - "reference": "2d814cd25e299be022a4fa0fb4509d7ccd353d8a", + "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/ee3b72b19ccdf57d0243116ecb2b90261344dedc", + "reference": "ee3b72b19ccdf57d0243116ecb2b90261344dedc", "shasum": "" }, "require": { "dompdf/dompdf": "^3.0", - "illuminate/support": "^9|^10|^11", + "illuminate/support": "^9|^10|^11|^12|^13.0", "php": "^8.1" }, "require-dev": { - "larastan/larastan": "^2.7.0", - "orchestra/testbench": "^7|^8|^9", + "larastan/larastan": "^2.7|^3.0", + "orchestra/testbench": "^7|^8|^9.16|^10|^11.0", "phpro/grumphp": "^2.5", "squizlabs/php_codesniffer": "^3.5" }, @@ -69,7 +69,7 @@ ], "support": { "issues": "https://github.com/barryvdh/laravel-dompdf/issues", - "source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.0.0" + "source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.2" }, "funding": [ { @@ -81,29 +81,29 @@ "type": "github" } ], - "time": "2024-07-31T14:45:16+00:00" + "time": "2026-02-21T08:51:10+00:00" }, { "name": "brick/math", - "version": "0.12.1", + "version": "0.14.8", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1" + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f510c0a40911935b77b86859eb5223d58d660df1", - "reference": "f510c0a40911935b77b86859eb5223d58d660df1", + "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629", + "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629", "shasum": "" }, "require": { - "php": "^8.1" + "php": "^8.2" }, "require-dev": { "php-coveralls/php-coveralls": "^2.2", - "phpunit/phpunit": "^10.1", - "vimeo/psalm": "5.16.0" + "phpstan/phpstan": "2.1.22", + "phpunit/phpunit": "^11.5" }, "type": "library", "autoload": { @@ -133,7 +133,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.12.1" + "source": "https://github.com/brick/math/tree/0.14.8" }, "funding": [ { @@ -141,7 +141,7 @@ "type": "github" } ], - "time": "2023-11-29T23:19:16+00:00" + "time": "2026-02-10T14:33:43+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -212,6 +212,162 @@ ], "time": "2024-02-09T16:56:22+00:00" }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, + "require-dev": { + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" + }, + "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-11-12T16:29:46+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + } + ], + "time": "2025-08-20T19:15:30+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.3", @@ -289,33 +445,32 @@ }, { "name": "doctrine/inflector", - "version": "2.0.10", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", - "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^11.0", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.3", - "phpunit/phpunit": "^8.5 || ^9.5", - "vimeo/psalm": "^4.25 || ^5.4" + "doctrine/coding-standard": "^12.0 || ^13.0", + "phpstan/phpstan": "^1.12 || ^2.0", + "phpstan/phpstan-phpunit": "^1.4 || ^2.0", + "phpstan/phpstan-strict-rules": "^1.6 || ^2.0", + "phpunit/phpunit": "^8.5 || ^12.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + "Doctrine\\Inflector\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -360,7 +515,7 @@ ], "support": { "issues": "https://github.com/doctrine/inflector/issues", - "source": "https://github.com/doctrine/inflector/tree/2.0.10" + "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, "funding": [ { @@ -376,7 +531,7 @@ "type": "tidelift" } ], - "time": "2024-02-18T20:23:39+00:00" + "time": "2025-08-10T19:31:58+00:00" }, { "name": "doctrine/lexer", @@ -457,16 +612,16 @@ }, { "name": "dompdf/dompdf", - "version": "v3.0.1", + "version": "v3.1.5", "source": { "type": "git", "url": "https://github.com/dompdf/dompdf.git", - "reference": "2d622faf9aa1f8f7f24dd094e49b5cf6c0c5d4e6" + "reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/2d622faf9aa1f8f7f24dd094e49b5cf6c0c5d4e6", - "reference": "2d622faf9aa1f8f7f24dd094e49b5cf6c0c5d4e6", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496", + "reference": "f11ead23a8a76d0ff9bbc6c7c8fd7e05ca328496", "shasum": "" }, "require": { @@ -482,7 +637,7 @@ "ext-json": "*", "ext-zip": "*", "mockery/mockery": "^1.3", - "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10", + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11", "squizlabs/php_codesniffer": "^3.5", "symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0" }, @@ -515,22 +670,22 @@ "homepage": "https://github.com/dompdf/dompdf", "support": { "issues": "https://github.com/dompdf/dompdf/issues", - "source": "https://github.com/dompdf/dompdf/tree/v3.0.1" + "source": "https://github.com/dompdf/dompdf/tree/v3.1.5" }, - "time": "2024-12-05T14:59:38+00:00" + "time": "2026-03-03T13:54:37+00:00" }, { "name": "dompdf/php-font-lib", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/dompdf/php-font-lib.git", - "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d" + "reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", - "reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d", + "url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/a6e9a688a2a80016ac080b97be73d3e10c444c9a", + "reference": "a6e9a688a2a80016ac080b97be73d3e10c444c9a", "shasum": "" }, "require": { @@ -538,7 +693,7 @@ "php": "^7.1 || ^8.0" }, "require-dev": { - "symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6" + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11 || ^12" }, "type": "library", "autoload": { @@ -560,31 +715,31 @@ "homepage": "https://github.com/dompdf/php-font-lib", "support": { "issues": "https://github.com/dompdf/php-font-lib/issues", - "source": "https://github.com/dompdf/php-font-lib/tree/1.0.1" + "source": "https://github.com/dompdf/php-font-lib/tree/1.0.2" }, - "time": "2024-12-02T14:37:59+00:00" + "time": "2026-01-20T14:10:26+00:00" }, { "name": "dompdf/php-svg-lib", - "version": "1.0.0", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/dompdf/php-svg-lib.git", - "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af" + "reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af", - "reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af", + "url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/8259ffb930817e72b1ff1caef5d226501f3dfeb1", + "reference": "8259ffb930817e72b1ff1caef5d226501f3dfeb1", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^7.1 || ^8.0", - "sabberworm/php-css-parser": "^8.4" + "sabberworm/php-css-parser": "^8.4 || ^9.0" }, "require-dev": { - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5" + "phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11" }, "type": "library", "autoload": { @@ -606,35 +761,34 @@ "homepage": "https://github.com/dompdf/php-svg-lib", "support": { "issues": "https://github.com/dompdf/php-svg-lib/issues", - "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0" + "source": "https://github.com/dompdf/php-svg-lib/tree/1.0.2" }, - "time": "2024-04-29T13:26:35+00:00" + "time": "2026-01-02T16:01:13+00:00" }, { "name": "dragonmantank/cron-expression", - "version": "v3.4.0", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/dragonmantank/cron-expression.git", - "reference": "8c784d071debd117328803d86b2097615b457500" + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/8c784d071debd117328803d86b2097615b457500", - "reference": "8c784d071debd117328803d86b2097615b457500", + "url": "https://api.github.com/repos/dragonmantank/cron-expression/zipball/d61a8a9604ec1f8c3d150d09db6ce98b32675013", + "reference": "d61a8a9604ec1f8c3d150d09db6ce98b32675013", "shasum": "" }, "require": { - "php": "^7.2|^8.0", - "webmozart/assert": "^1.0" + "php": "^8.2|^8.3|^8.4|^8.5" }, "replace": { "mtdowling/cron-expression": "^1.0" }, "require-dev": { - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.0", - "phpunit/phpunit": "^7.0|^8.0|^9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.32|^2.1.31", + "phpunit/phpunit": "^8.5.48|^9.0" }, "type": "library", "extra": { @@ -665,7 +819,7 @@ ], "support": { "issues": "https://github.com/dragonmantank/cron-expression/issues", - "source": "https://github.com/dragonmantank/cron-expression/tree/v3.4.0" + "source": "https://github.com/dragonmantank/cron-expression/tree/v3.6.0" }, "funding": [ { @@ -673,20 +827,20 @@ "type": "github" } ], - "time": "2024-10-09T13:47:03+00:00" + "time": "2025-10-31T18:51:33+00:00" }, { "name": "egulias/email-validator", - "version": "4.0.2", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e" + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ebaaf5be6c0286928352e054f2d5125608e5405e", - "reference": "ebaaf5be6c0286928352e054f2d5125608e5405e", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", + "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa", "shasum": "" }, "require": { @@ -732,7 +886,7 @@ ], "support": { "issues": "https://github.com/egulias/EmailValidator/issues", - "source": "https://github.com/egulias/EmailValidator/tree/4.0.2" + "source": "https://github.com/egulias/EmailValidator/tree/4.0.4" }, "funding": [ { @@ -740,35 +894,96 @@ "type": "github" } ], - "time": "2023-10-06T06:47:41+00:00" + "time": "2025-03-06T22:45:56+00:00" }, { - "name": "fruitcake/php-cors", - "version": "v1.3.0", + "name": "ezyang/htmlpurifier", + "version": "v4.19.0", "source": { "type": "git", - "url": "https://github.com/fruitcake/php-cors.git", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b" + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/3d158f36e7875e2f040f37bc0573956240a5a38b", - "reference": "3d158f36e7875e2f040f37bc0573956240a5a38b", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/b287d2a16aceffbf6e0295559b39662612b77fcf", + "reference": "b287d2a16aceffbf6e0295559b39662612b77fcf", "shasum": "" }, "require": { - "php": "^7.4|^8.0", - "symfony/http-foundation": "^4.4|^5.4|^6|^7" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", + "cerdic/css-tidy": "^1.7 || ^2.0", + "simpletest/simpletest": "dev-master" + }, + "suggest": { + "cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.", + "ext-bcmath": "Used for unit conversion and imagecrash protection", + "ext-iconv": "Converts text to and from non-UTF-8 encodings", + "ext-tidy": "Used for pretty-printing HTML" + }, + "type": "library", + "autoload": { + "files": [ + "library/HTMLPurifier.composer.php" + ], + "psr-0": { + "HTMLPurifier": "library/" + }, + "exclude-from-classmap": [ + "/library/HTMLPurifier/Language/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1-or-later" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "support": { + "issues": "https://github.com/ezyang/htmlpurifier/issues", + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.19.0" + }, + "time": "2025-10-17T16:34:55+00:00" + }, + { + "name": "fruitcake/php-cors", + "version": "v1.4.0", + "source": { + "type": "git", + "url": "https://github.com/fruitcake/php-cors.git", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/http-foundation": "^5.4|^6.4|^7.3|^8" + }, + "require-dev": { + "phpstan/phpstan": "^2", "phpunit/phpunit": "^9", - "squizlabs/php_codesniffer": "^3.5" + "squizlabs/php_codesniffer": "^4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2-dev" + "dev-master": "1.3-dev" } }, "autoload": { @@ -799,7 +1014,7 @@ ], "support": { "issues": "https://github.com/fruitcake/php-cors/issues", - "source": "https://github.com/fruitcake/php-cors/tree/v1.3.0" + "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" }, "funding": [ { @@ -811,28 +1026,28 @@ "type": "github" } ], - "time": "2023-10-12T05:21:21+00:00" + "time": "2025-12-03T09:33:47+00:00" }, { "name": "graham-campbell/result-type", - "version": "v1.1.3", + "version": "v1.1.4", "source": { "type": "git", "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945" + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/3ba905c11371512af9d9bdd27d99b782216b6945", - "reference": "3ba905c11371512af9d9bdd27d99b782216b6945", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3" + "phpoption/phpoption": "^1.9.5" }, "require-dev": { - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.41 || ^9.6.22 || ^10.5.45 || ^11.5.7" }, "type": "library", "autoload": { @@ -861,7 +1076,7 @@ ], "support": { "issues": "https://github.com/GrahamCampbell/Result-Type/issues", - "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.3" + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" }, "funding": [ { @@ -873,26 +1088,26 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:45:45+00:00" + "time": "2025-12-27T19:43:20+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.9.2", + "version": "7.10.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b" + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", - "reference": "d281ed313b989f213357e3be1a179f02196ac99b", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.3", - "guzzlehttp/psr7": "^2.7.0", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -983,7 +1198,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.9.2" + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" }, "funding": [ { @@ -999,20 +1214,20 @@ "type": "tidelift" } ], - "time": "2024-07-24T11:22:20+00:00" + "time": "2025-08-23T22:36:01+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.4", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" + "reference": "481557b130ef3790cf82b713667b43030dc9c957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", - "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "url": "https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", "shasum": "" }, "require": { @@ -1020,7 +1235,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "type": "library", "extra": { @@ -1066,7 +1281,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.4" + "source": "https://github.com/guzzle/promises/tree/2.3.0" }, "funding": [ { @@ -1082,20 +1297,20 @@ "type": "tidelift" } ], - "time": "2024-10-17T10:06:22+00:00" + "time": "2025-08-22T14:34:08+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.7.0", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", - "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884", + "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884", "shasum": "" }, "require": { @@ -1111,7 +1326,8 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "0.9.0", - "phpunit/phpunit": "^8.5.39 || ^9.6.20" + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1182,7 +1398,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.7.0" + "source": "https://github.com/guzzle/psr7/tree/2.9.0" }, "funding": [ { @@ -1198,20 +1414,20 @@ "type": "tidelift" } ], - "time": "2024-07-18T11:15:46+00:00" + "time": "2026-03-10T16:41:02+00:00" }, { "name": "guzzlehttp/uri-template", - "version": "v1.0.3", + "version": "v1.0.5", "source": { "type": "git", "url": "https://github.com/guzzle/uri-template.git", - "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c" + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/ecea8feef63bd4fef1f037ecb288386999ecc11c", - "reference": "ecea8feef63bd4fef1f037ecb288386999ecc11c", + "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, "require": { @@ -1220,7 +1436,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.44 || ^9.6.25", "uri-template/tests": "1.0.0" }, "type": "library", @@ -1268,7 +1484,7 @@ ], "support": { "issues": "https://github.com/guzzle/uri-template/issues", - "source": "https://github.com/guzzle/uri-template/tree/v1.0.3" + "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, "funding": [ { @@ -1284,28 +1500,28 @@ "type": "tidelift" } ], - "time": "2023-12-03T19:50:20+00:00" + "time": "2025-08-22T14:27:06+00:00" }, { "name": "intervention/gif", - "version": "4.2.0", + "version": "4.2.4", "source": { "type": "git", "url": "https://github.com/Intervention/gif.git", - "reference": "42c131a31b93c440ad49061b599fa218f06f93be" + "reference": "c3598a16ebe7690cd55640c44144a9df383ea73c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/gif/zipball/42c131a31b93c440ad49061b599fa218f06f93be", - "reference": "42c131a31b93c440ad49061b599fa218f06f93be", + "url": "https://api.github.com/repos/Intervention/gif/zipball/c3598a16ebe7690cd55640c44144a9df383ea73c", + "reference": "c3598a16ebe7690cd55640c44144a9df383ea73c", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { - "phpstan/phpstan": "^1", - "phpunit/phpunit": "^10.0", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", "slevomat/coding-standard": "~8.0", "squizlabs/php_codesniffer": "^3.8" }, @@ -1336,7 +1552,7 @@ ], "support": { "issues": "https://github.com/Intervention/gif/issues", - "source": "https://github.com/Intervention/gif/tree/4.2.0" + "source": "https://github.com/Intervention/gif/tree/4.2.4" }, "funding": [ { @@ -1352,20 +1568,20 @@ "type": "ko_fi" } ], - "time": "2024-09-20T13:35:02+00:00" + "time": "2026-01-04T09:27:23+00:00" }, { "name": "intervention/image", - "version": "3.9.1", + "version": "3.11.8", "source": { "type": "git", "url": "https://github.com/Intervention/image.git", - "reference": "b496d1f6b9f812f96166623358dfcafb8c3b1683" + "reference": "cf04c8dd245697f701057c13d4bfe140d584e738" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Intervention/image/zipball/b496d1f6b9f812f96166623358dfcafb8c3b1683", - "reference": "b496d1f6b9f812f96166623358dfcafb8c3b1683", + "url": "https://api.github.com/repos/Intervention/image/zipball/cf04c8dd245697f701057c13d4bfe140d584e738", + "reference": "cf04c8dd245697f701057c13d4bfe140d584e738", "shasum": "" }, "require": { @@ -1375,10 +1591,10 @@ }, "require-dev": { "mockery/mockery": "^1.6", - "phpstan/phpstan": "^1", - "phpunit/phpunit": "^10.0", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^10.0 || ^11.0 || ^12.0", "slevomat/coding-standard": "~8.0", - "squizlabs/php_codesniffer": "^3.8" + "squizlabs/php_codesniffer": "^4" }, "suggest": { "ext-exif": "Recommended to be able to read EXIF data properly." @@ -1397,11 +1613,11 @@ { "name": "Oliver Vogel", "email": "oliver@intervention.io", - "homepage": "https://intervention.io/" + "homepage": "https://intervention.io" } ], - "description": "PHP image manipulation", - "homepage": "https://image.intervention.io/", + "description": "PHP Image Processing", + "homepage": "https://image.intervention.io", "keywords": [ "gd", "image", @@ -1412,7 +1628,7 @@ ], "support": { "issues": "https://github.com/Intervention/image/issues", - "source": "https://github.com/Intervention/image/tree/3.9.1" + "source": "https://github.com/Intervention/image/tree/3.11.8" }, "funding": [ { @@ -1428,24 +1644,24 @@ "type": "ko_fi" } ], - "time": "2024-10-27T10:15:54+00:00" + "time": "2026-05-01T08:20:10+00:00" }, { "name": "laminas/laminas-diactoros", - "version": "3.5.0", + "version": "3.8.0", "source": { "type": "git", "url": "https://github.com/laminas/laminas-diactoros.git", - "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2" + "reference": "60c182916b2749480895601649563970f3f12ec4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/143a16306602ce56b8b092a7914fef03c37f9ed2", - "reference": "143a16306602ce56b8b092a7914fef03c37f9ed2", + "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/60c182916b2749480895601649563970f3f12ec4", + "reference": "60c182916b2749480895601649563970f3f12ec4", "shasum": "" }, "require": { - "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", "psr/http-factory": "^1.1", "psr/http-message": "^1.1 || ^2.0" }, @@ -1462,11 +1678,11 @@ "ext-gd": "*", "ext-libxml": "*", "http-interop/http-factory-tests": "^2.2.0", - "laminas/laminas-coding-standard": "~2.5.0", + "laminas/laminas-coding-standard": "~3.1.0", "php-http/psr7-integration-tests": "^1.4.0", "phpunit/phpunit": "^10.5.36", - "psalm/plugin-phpunit": "^0.19.0", - "vimeo/psalm": "^5.26.1" + "psalm/plugin-phpunit": "^0.19.5", + "vimeo/psalm": "^6.13" }, "type": "library", "extra": { @@ -1516,24 +1732,24 @@ "type": "community_bridge" } ], - "time": "2024-10-14T11:59:49+00:00" + "time": "2025-10-12T15:31:36+00:00" }, { "name": "laravel/framework", - "version": "v11.35.1", + "version": "v11.51.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "dcfa130ede1a6fa4343dc113410963e791ad34fb" + "reference": "c8f9a04594b7044a189a3194cfb3594251eb74e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/dcfa130ede1a6fa4343dc113410963e791ad34fb", - "reference": "dcfa130ede1a6fa4343dc113410963e791ad34fb", + "url": "https://api.github.com/repos/laravel/framework/zipball/c8f9a04594b7044a189a3194cfb3594251eb74e5", + "reference": "c8f9a04594b7044a189a3194cfb3594251eb74e5", "shasum": "" }, "require": { - "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12", + "brick/math": "^0.9.3|^0.10.2|^0.11|^0.12|^0.13|^0.14", "composer-runtime-api": "^2.2", "doctrine/inflector": "^2.0.5", "dragonmantank/cron-expression": "^3.4", @@ -1550,12 +1766,12 @@ "guzzlehttp/uri-template": "^1.0", "laravel/prompts": "^0.1.18|^0.2.0|^0.3.0", "laravel/serializable-closure": "^1.3|^2.0", - "league/commonmark": "^2.2.1", + "league/commonmark": "^2.7", "league/flysystem": "^3.25.1", "league/flysystem-local": "^3.25.1", "league/uri": "^7.5.1", "monolog/monolog": "^3.0", - "nesbot/carbon": "^2.72.2|^3.4", + "nesbot/carbon": "^2.72.6|^3.8.4", "nunomaduro/termwind": "^2.0", "php": "^8.2", "psr/container": "^1.1.1|^2.0.1", @@ -1565,7 +1781,7 @@ "symfony/console": "^7.0.3", "symfony/error-handler": "^7.0.3", "symfony/finder": "^7.0.3", - "symfony/http-foundation": "^7.0.3", + "symfony/http-foundation": "^7.2.0", "symfony/http-kernel": "^7.0.3", "symfony/mailer": "^7.0.3", "symfony/mime": "^7.0.3", @@ -1579,7 +1795,6 @@ "voku/portable-ascii": "^2.0.2" }, "conflict": { - "mockery/mockery": "1.6.8", "tightenco/collect": "<5.5.33" }, "provide": { @@ -1631,17 +1846,18 @@ "fakerphp/faker": "^1.24", "guzzlehttp/promises": "^2.0.3", "guzzlehttp/psr7": "^2.4", + "laravel/pint": "^1.18", "league/flysystem-aws-s3-v3": "^3.25.1", "league/flysystem-ftp": "^3.25.1", "league/flysystem-path-prefixing": "^3.25.1", "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "orchestra/testbench-core": "^9.6", + "orchestra/testbench-core": "^9.18.0", "pda/pheanstalk": "^5.0.6", "php-http/discovery": "^1.15", - "phpstan/phpstan": "^1.11.5", - "phpunit/phpunit": "^10.5.35|^11.3.6", + "phpstan/phpstan": "2.1.41", + "phpunit/phpunit": "^10.5.35|^11.3.6|^12.0.1", "predis/predis": "^2.3", "resend/resend-php": "^0.10.0", "symfony/cache": "^7.0.3", @@ -1673,7 +1889,7 @@ "mockery/mockery": "Required to use mocking (^1.6).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", - "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).", + "phpunit/phpunit": "Required to use assertions and run tests (^10.5.35|^11.3.6|^12.0.1).", "predis/predis": "Required to use the predis connector (^2.3).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).", @@ -1731,31 +1947,31 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-12-12T18:25:58+00:00" + "time": "2026-03-26T14:54:53+00:00" }, { "name": "laravel/octane", - "version": "v2.6.1", + "version": "v2.17.3", "source": { "type": "git", "url": "https://github.com/laravel/octane.git", - "reference": "6d46153a04d73d07466bbb1f70d827614dc131eb" + "reference": "040bfbebfdf545a752376f4396d942cd6f7da036" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/octane/zipball/6d46153a04d73d07466bbb1f70d827614dc131eb", - "reference": "6d46153a04d73d07466bbb1f70d827614dc131eb", + "url": "https://api.github.com/repos/laravel/octane/zipball/040bfbebfdf545a752376f4396d942cd6f7da036", + "reference": "040bfbebfdf545a752376f4396d942cd6f7da036", "shasum": "" }, "require": { "laminas/laminas-diactoros": "^3.0", - "laravel/framework": "^10.10.1|^11.0", + "laravel/framework": "^10.10.1|^11.0|^12.0|^13.0", "laravel/prompts": "^0.1.24|^0.2.0|^0.3.0", "laravel/serializable-closure": "^1.3|^2.0", "nesbot/carbon": "^2.66.0|^3.0", "php": "^8.1.0", - "symfony/console": "^6.0|^7.0", - "symfony/psr-http-message-bridge": "^2.2.0|^6.4|^7.0" + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/psr-http-message-bridge": "^2.2.0|^6.4|^7.0|^8.0" }, "conflict": { "spiral/roadrunner": "<2023.1.0", @@ -1768,11 +1984,10 @@ "laravel/scout": "^10.2.1", "laravel/socialite": "^5.6.1", "livewire/livewire": "^2.12.3|^3.0", - "mockery/mockery": "^1.5.1", "nunomaduro/collision": "^6.4.0|^7.5.2|^8.0", - "orchestra/testbench": "^8.21|^9.0", - "phpstan/phpstan": "^1.10.15", - "phpunit/phpunit": "^10.4", + "orchestra/testbench": "^8.21|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.1.7", + "phpunit/phpunit": "^10.4|^11.5|^12.0|^13.0", "spiral/roadrunner-cli": "^2.6.0", "spiral/roadrunner-http": "^3.3.0" }, @@ -1821,38 +2036,38 @@ "issues": "https://github.com/laravel/octane/issues", "source": "https://github.com/laravel/octane" }, - "time": "2025-01-28T15:16:08+00:00" + "time": "2026-04-23T14:52:16+00:00" }, { "name": "laravel/prompts", - "version": "v0.3.2", + "version": "v0.3.17", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "0e0535747c6b8d6d10adca8b68293cf4517abb0f" + "reference": "6a82ac19a28b916ae0885828795dbd4c59d9a818" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/0e0535747c6b8d6d10adca8b68293cf4517abb0f", - "reference": "0e0535747c6b8d6d10adca8b68293cf4517abb0f", + "url": "https://api.github.com/repos/laravel/prompts/zipball/6a82ac19a28b916ae0885828795dbd4c59d9a818", + "reference": "6a82ac19a28b916ae0885828795dbd4c59d9a818", "shasum": "" }, "require": { "composer-runtime-api": "^2.2", "ext-mbstring": "*", "php": "^8.1", - "symfony/console": "^6.2|^7.0" + "symfony/console": "^6.2|^7.0|^8.0" }, "conflict": { "illuminate/console": ">=10.17.0 <10.25.0", "laravel/framework": ">=10.17.0 <10.25.0" }, "require-dev": { - "illuminate/collections": "^10.0|^11.0", + "illuminate/collections": "^10.0|^11.0|^12.0|^13.0", "mockery/mockery": "^1.5", - "pestphp/pest": "^2.3|^3.4", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-mockery": "^1.1" + "pestphp/pest": "^2.3|^3.4|^4.0", + "phpstan/phpstan": "^1.12.28", + "phpstan/phpstan-mockery": "^1.1.3" }, "suggest": { "ext-pcntl": "Required for the spinner to be animated." @@ -1878,33 +2093,33 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.2" + "source": "https://github.com/laravel/prompts/tree/v0.3.17" }, - "time": "2024-11-12T14:59:47+00:00" + "time": "2026-04-20T16:07:33+00:00" }, { "name": "laravel/serializable-closure", - "version": "v2.0.0", + "version": "v2.0.13", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "0d8d3d8086984996df86596a86dea60398093a81" + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/0d8d3d8086984996df86596a86dea60398093a81", - "reference": "0d8d3d8086984996df86596a86dea60398093a81", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/b566ee0dd251f3c4078bed003a7ce015f5ea6dce", + "reference": "b566ee0dd251f3c4078bed003a7ce015f5ea6dce", "shasum": "" }, "require": { "php": "^8.1" }, "require-dev": { - "illuminate/support": "^10.0|^11.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", "nesbot/carbon": "^2.67|^3.0", - "pestphp/pest": "^2.36", + "pestphp/pest": "^2.36|^3.0|^4.0", "phpstan/phpstan": "^2.0", - "symfony/var-dumper": "^6.2.0|^7.0.0" + "symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0" }, "type": "library", "extra": { @@ -1941,37 +2156,37 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2024-11-19T01:38:44+00:00" + "time": "2026-04-16T14:03:50+00:00" }, { "name": "laravel/tinker", - "version": "v2.10.0", + "version": "v2.11.1", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "ba4d51eb56de7711b3a37d63aa0643e99a339ae5" + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/ba4d51eb56de7711b3a37d63aa0643e99a339ae5", - "reference": "ba4d51eb56de7711b3a37d63aa0643e99a339ae5", + "url": "https://api.github.com/repos/laravel/tinker/zipball/c9f80cc835649b5c1842898fb043f8cc098dd741", + "reference": "c9f80cc835649b5c1842898fb043f8cc098dd741", "shasum": "" }, "require": { - "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", - "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0", + "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", + "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0", "php": "^7.2.5|^8.0", "psy/psysh": "^0.11.1|^0.12.0", - "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0" + "symfony/var-dumper": "^4.3.4|^5.0|^6.0|^7.0|^8.0" }, "require-dev": { "mockery/mockery": "~1.3.3|^1.4.2", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^8.5.8|^9.3.3" + "phpunit/phpunit": "^8.5.8|^9.3.3|^10.0" }, "suggest": { - "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0)." + "illuminate/database": "The Illuminate Database package (^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0)." }, "type": "library", "extra": { @@ -2005,45 +2220,45 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.0" + "source": "https://github.com/laravel/tinker/tree/v2.11.1" }, - "time": "2024-09-23T13:32:56+00:00" + "time": "2026-02-06T14:12:35+00:00" }, { "name": "laravel/ui", - "version": "v4.6.0", + "version": "v4.6.3", "source": { "type": "git", "url": "https://github.com/laravel/ui.git", - "reference": "a34609b15ae0c0512a0cf47a21695a2729cb7f93" + "reference": "ff27db15416c1ed8ad9848f5692e47595dd5de27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/ui/zipball/a34609b15ae0c0512a0cf47a21695a2729cb7f93", - "reference": "a34609b15ae0c0512a0cf47a21695a2729cb7f93", + "url": "https://api.github.com/repos/laravel/ui/zipball/ff27db15416c1ed8ad9848f5692e47595dd5de27", + "reference": "ff27db15416c1ed8ad9848f5692e47595dd5de27", "shasum": "" }, "require": { - "illuminate/console": "^9.21|^10.0|^11.0", - "illuminate/filesystem": "^9.21|^10.0|^11.0", - "illuminate/support": "^9.21|^10.0|^11.0", - "illuminate/validation": "^9.21|^10.0|^11.0", + "illuminate/console": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/filesystem": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.21|^10.0|^11.0|^12.0|^13.0", + "illuminate/validation": "^9.21|^10.0|^11.0|^12.0|^13.0", "php": "^8.0", - "symfony/console": "^6.0|^7.0" + "symfony/console": "^6.0|^7.0|^8.0" }, "require-dev": { - "orchestra/testbench": "^7.35|^8.15|^9.0", - "phpunit/phpunit": "^9.3|^10.4|^11.0" + "orchestra/testbench": "^7.35|^8.15|^9.0|^10.0|^11.0", + "phpunit/phpunit": "^9.3|^10.4|^11.5|^12.5|^13.0" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "4.x-dev" - }, "laravel": { "providers": [ "Laravel\\Ui\\UiServiceProvider" ] + }, + "branch-alias": { + "dev-master": "4.x-dev" } }, "autoload": { @@ -2068,36 +2283,36 @@ "ui" ], "support": { - "source": "https://github.com/laravel/ui/tree/v4.6.0" + "source": "https://github.com/laravel/ui/tree/v4.6.3" }, - "time": "2024-11-21T15:06:41+00:00" + "time": "2026-03-17T13:41:52+00:00" }, { "name": "laravolt/avatar", - "version": "6.0.0", + "version": "6.4.0", "source": { "type": "git", "url": "https://github.com/laravolt/avatar.git", - "reference": "60673413db93f1491c2eb556e1c2050de14fba5c" + "reference": "fbe1a25ef9eee8daf4e43f29d0bc62bd34442b08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravolt/avatar/zipball/60673413db93f1491c2eb556e1c2050de14fba5c", - "reference": "60673413db93f1491c2eb556e1c2050de14fba5c", + "url": "https://api.github.com/repos/laravolt/avatar/zipball/fbe1a25ef9eee8daf4e43f29d0bc62bd34442b08", + "reference": "fbe1a25ef9eee8daf4e43f29d0bc62bd34442b08", "shasum": "" }, "require": { - "illuminate/cache": "^10.0|^11.0", - "illuminate/support": "^10.0|^11.0", + "illuminate/cache": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", "intervention/image": "^3.4", - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "mockery/mockery": "~1.3", - "pestphp/pest": "^2.34", - "pestphp/pest-plugin-type-coverage": "^2.8", + "mockery/mockery": "^1.6.7", + "pestphp/pest": "^2.34|^3.7", + "pestphp/pest-plugin-type-coverage": "^2.8|^3.3", "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^10.5", + "phpunit/phpunit": "^11.5.3", "roave/security-advisories": "dev-latest" }, "suggest": { @@ -2145,7 +2360,7 @@ ], "support": { "issues": "https://github.com/laravolt/avatar/issues", - "source": "https://github.com/laravolt/avatar/tree/6.0.0" + "source": "https://github.com/laravolt/avatar/tree/6.4.0" }, "funding": [ { @@ -2161,20 +2376,20 @@ "type": "patreon" } ], - "time": "2024-09-20T17:07:45+00:00" + "time": "2026-03-06T19:35:59+00:00" }, { "name": "league/commonmark", - "version": "2.6.0", + "version": "2.8.2", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "d150f911e0079e90ae3c106734c93137c184f932" + "reference": "59fb075d2101740c337c7216e3f32b36c204218b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/d150f911e0079e90ae3c106734c93137c184f932", - "reference": "d150f911e0079e90ae3c106734c93137c184f932", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/59fb075d2101740c337c7216e3f32b36c204218b", + "reference": "59fb075d2101740c337c7216e3f32b36c204218b", "shasum": "" }, "require": { @@ -2199,11 +2414,11 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 | ^7.0", - "symfony/process": "^5.4 | ^6.0 | ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0 || ^8.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0 || ^8.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0 || ^8.0", "unleashedtech/php-coding-standard": "^3.1.1", - "vimeo/psalm": "^4.24.0 || ^5.0.0" + "vimeo/psalm": "^4.24.0 || ^5.0.0 || ^6.0.0" }, "suggest": { "symfony/yaml": "v2.3+ required if using the Front Matter extension" @@ -2211,7 +2426,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.7-dev" + "dev-main": "2.9-dev" } }, "autoload": { @@ -2268,7 +2483,7 @@ "type": "tidelift" } ], - "time": "2024-12-07T15:34:16+00:00" + "time": "2026-03-19T13:16:38+00:00" }, { "name": "league/config", @@ -2354,16 +2569,16 @@ }, { "name": "league/flysystem", - "version": "3.29.1", + "version": "3.33.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319" + "reference": "570b8871e0ce693764434b29154c54b434905350" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319", - "reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/570b8871e0ce693764434b29154c54b434905350", + "reference": "570b8871e0ce693764434b29154c54b434905350", "shasum": "" }, "require": { @@ -2387,13 +2602,13 @@ "composer/semver": "^3.0", "ext-fileinfo": "*", "ext-ftp": "*", - "ext-mongodb": "^1.3", + "ext-mongodb": "^1.3|^2", "ext-zip": "*", "friendsofphp/php-cs-fixer": "^3.5", "google/cloud-storage": "^1.23", "guzzlehttp/psr7": "^2.6", "microsoft/azure-storage-blob": "^1.1", - "mongodb/mongodb": "^1.2", + "mongodb/mongodb": "^1.2|^2", "phpseclib/phpseclib": "^3.0.36", "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^9.5.11|^10.0", @@ -2431,22 +2646,22 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.29.1" + "source": "https://github.com/thephpleague/flysystem/tree/3.33.0" }, - "time": "2024-10-08T08:58:34+00:00" + "time": "2026-03-25T07:59:30+00:00" }, { "name": "league/flysystem-local", - "version": "3.29.0", + "version": "3.31.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-local.git", - "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27" + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27", - "reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27", + "url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/2f669db18a4c20c755c2bb7d3a7b0b2340488079", + "reference": "2f669db18a4c20c755c2bb7d3a7b0b2340488079", "shasum": "" }, "require": { @@ -2480,22 +2695,22 @@ "local" ], "support": { - "source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem-local/tree/3.31.0" }, - "time": "2024-08-09T21:24:39+00:00" + "time": "2026-01-23T15:30:45+00:00" }, { "name": "league/flysystem-webdav", - "version": "3.29.0", + "version": "3.31.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem-webdav.git", - "reference": "08bea4cad465a3751410614fc777f62e8c96a8e5" + "reference": "1705b5d4aab6d755a78ea850e523dda9c58ad20f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem-webdav/zipball/08bea4cad465a3751410614fc777f62e8c96a8e5", - "reference": "08bea4cad465a3751410614fc777f62e8c96a8e5", + "url": "https://api.github.com/repos/thephpleague/flysystem-webdav/zipball/1705b5d4aab6d755a78ea850e523dda9c58ad20f", + "reference": "1705b5d4aab6d755a78ea850e523dda9c58ad20f", "shasum": "" }, "require": { @@ -2528,9 +2743,9 @@ "filesystem" ], "support": { - "source": "https://github.com/thephpleague/flysystem-webdav/tree/3.29.0" + "source": "https://github.com/thephpleague/flysystem-webdav/tree/3.31.0" }, - "time": "2024-08-09T21:24:39+00:00" + "time": "2026-01-23T15:30:45+00:00" }, { "name": "league/mime-type-detection", @@ -2590,33 +2805,38 @@ }, { "name": "league/uri", - "version": "7.5.1", + "version": "7.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "81fb5145d2644324614cc532b28efd0215bda430" + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", - "reference": "81fb5145d2644324614cc532b28efd0215bda430", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/08cf38e3924d4f56238125547b5720496fac8fd4", + "reference": "08cf38e3924d4f56238125547b5720496fac8fd4", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.5", - "php": "^8.1" + "league/uri-interfaces": "^7.8.1", + "php": "^8.1", + "psr/http-factory": "^1" }, "conflict": { "league/uri-schemes": "^1.0" }, "suggest": { "ext-bcmath": "to improve IPV4 host parsing", + "ext-dom": "to convert the URI into an HTML anchor tag", "ext-fileinfo": "to create Data URI from file contennts", "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", - "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", - "league/uri-components": "Needed to easily manipulate URI objects components", + "ext-uri": "to use the PHP native URI class", + "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", + "league/uri-components": "to provide additional tools to manipulate URI objects components", + "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2644,6 +2864,7 @@ "description": "URI manipulation library", "homepage": "https://uri.thephpleague.com", "keywords": [ + "URN", "data-uri", "file-uri", "ftp", @@ -2656,9 +2877,11 @@ "psr-7", "query-string", "querystring", + "rfc2141", "rfc3986", "rfc3987", "rfc6570", + "rfc8141", "uri", "uri-template", "url", @@ -2668,7 +2891,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.5.1" + "source": "https://github.com/thephpleague/uri/tree/7.8.1" }, "funding": [ { @@ -2676,26 +2899,25 @@ "type": "github" } ], - "time": "2024-12-08T08:40:02+00:00" + "time": "2026-03-15T20:22:25+00:00" }, { "name": "league/uri-interfaces", - "version": "7.5.0", + "version": "7.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", - "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/85d5c77c5d6d3af6c54db4a78246364908f3c928", + "reference": "85d5c77c5d6d3af6c54db4a78246364908f3c928", "shasum": "" }, "require": { "ext-filter": "*", "php": "^8.1", - "psr/http-factory": "^1", "psr/http-message": "^1.1 || ^2.0" }, "suggest": { @@ -2703,6 +2925,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", + "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -2727,7 +2950,7 @@ "homepage": "https://nyamsprod.com" } ], - "description": "Common interfaces and classes for URI representation and interaction", + "description": "Common tools for parsing and resolving RFC3987/RFC3986 URI", "homepage": "https://uri.thephpleague.com", "keywords": [ "data-uri", @@ -2752,7 +2975,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.1" }, "funding": [ { @@ -2760,35 +2983,117 @@ "type": "github" } ], - "time": "2024-12-08T08:18:47+00:00" + "time": "2026-03-08T20:05:35+00:00" }, { - "name": "maennchen/zipstream-php", - "version": "3.1.1", + "name": "maatwebsite/excel", + "version": "3.1.69", "source": { "type": "git", - "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "6187e9cc4493da94b9b63eb2315821552015fca9" + "url": "https://github.com/SpartnerNL/Laravel-Excel.git", + "reference": "ae5d65b7c9a2fac43bff4d44f796ac95d7a8e760" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/6187e9cc4493da94b9b63eb2315821552015fca9", - "reference": "6187e9cc4493da94b9b63eb2315821552015fca9", + "url": "https://api.github.com/repos/SpartnerNL/Laravel-Excel/zipball/ae5d65b7c9a2fac43bff4d44f796ac95d7a8e760", + "reference": "ae5d65b7c9a2fac43bff4d44f796ac95d7a8e760", + "shasum": "" + }, + "require": { + "composer/semver": "^3.3", + "ext-json": "*", + "illuminate/support": "5.8.*||^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0||^13.0", + "php": "^7.0||^8.0", + "phpoffice/phpspreadsheet": "^1.30.4", + "psr/simple-cache": "^1.0||^2.0||^3.0" + }, + "require-dev": { + "laravel/scout": "^7.0||^8.0||^9.0||^10.0||^11.0", + "orchestra/testbench": "^6.0||^7.0||^8.0||^9.0||^10.0||^11.0", + "predis/predis": "^1.1" + }, + "type": "library", + "extra": { + "laravel": { + "aliases": { + "Excel": "Maatwebsite\\Excel\\Facades\\Excel" + }, + "providers": [ + "Maatwebsite\\Excel\\ExcelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Maatwebsite\\Excel\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Patrick Brouwers", + "email": "patrick@spartner.nl" + } + ], + "description": "Supercharged Excel exports and imports in Laravel", + "keywords": [ + "PHPExcel", + "batch", + "csv", + "excel", + "export", + "import", + "laravel", + "php", + "phpspreadsheet" + ], + "support": { + "issues": "https://github.com/SpartnerNL/Laravel-Excel/issues", + "source": "https://github.com/SpartnerNL/Laravel-Excel/tree/3.1.69" + }, + "funding": [ + { + "url": "https://laravel-excel.com/commercial-support", + "type": "custom" + }, + { + "url": "https://github.com/patrickbrouwers", + "type": "github" + } + ], + "time": "2026-04-30T20:03:58+00:00" + }, + { + "name": "maennchen/zipstream-php", + "version": "3.2.2", + "source": { + "type": "git", + "url": "https://github.com/maennchen/ZipStream-PHP.git", + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", + "reference": "77bebeb4c6c340bb3c11c843b2cffd8bbfde4d5e", "shasum": "" }, "require": { "ext-mbstring": "*", "ext-zlib": "*", - "php-64bit": "^8.1" + "php-64bit": "^8.3" }, "require-dev": { + "brianium/paratest": "^7.7", "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.16", + "friendsofphp/php-cs-fixer": "^3.86", "guzzlehttp/guzzle": "^7.5", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.5", - "phpunit/phpunit": "^10.0", - "vimeo/psalm": "^5.0" + "phpunit/phpunit": "^12.0", + "vimeo/psalm": "^6.0" }, "suggest": { "guzzlehttp/psr7": "^2.4", @@ -2829,7 +3134,7 @@ ], "support": { "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.1.1" + "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.2" }, "funding": [ { @@ -2837,7 +3142,7 @@ "type": "github" } ], - "time": "2024-10-10T12:33:01+00:00" + "time": "2026-04-11T18:38:28+00:00" }, { "name": "markbaker/complex", @@ -2948,16 +3253,16 @@ }, { "name": "masterminds/html5", - "version": "2.9.0", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/Masterminds/html5-php.git", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6" + "reference": "fcf91eb64359852f00d921887b219479b4f21251" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", - "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251", + "reference": "fcf91eb64359852f00d921887b219479b4f21251", "shasum": "" }, "require": { @@ -3009,22 +3314,22 @@ ], "support": { "issues": "https://github.com/Masterminds/html5-php/issues", - "source": "https://github.com/Masterminds/html5-php/tree/2.9.0" + "source": "https://github.com/Masterminds/html5-php/tree/2.10.0" }, - "time": "2024-03-31T07:05:07+00:00" + "time": "2025-07-25T09:04:22+00:00" }, { "name": "monolog/monolog", - "version": "3.8.1", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4" + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4", - "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", + "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", "shasum": "" }, "require": { @@ -3042,7 +3347,7 @@ "graylog2/gelf-php": "^1.4.2 || ^2.0", "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8", + "mongodb/mongodb": "^1.8 || ^2.0", "php-amqplib/php-amqplib": "~2.4 || ^3", "php-console/php-console": "^3.1.8", "phpstan/phpstan": "^2", @@ -3102,7 +3407,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.8.1" + "source": "https://github.com/Seldaek/monolog/tree/3.10.0" }, "funding": [ { @@ -3114,20 +3419,20 @@ "type": "tidelift" } ], - "time": "2024-12-05T17:15:07+00:00" + "time": "2026-01-02T08:56:05+00:00" }, { "name": "nesbot/carbon", - "version": "3.8.2", + "version": "3.11.4", "source": { "type": "git", - "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947" + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e1268cdbc486d97ce23fef2c666dc3c6b6de9947", - "reference": "e1268cdbc486d97ce23fef2c666dc3c6b6de9947", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/e890471a3494740f7d9326d72ce6a8c559ffee60", + "reference": "e890471a3494740f7d9326d72ce6a8c559ffee60", "shasum": "" }, "require": { @@ -3135,9 +3440,9 @@ "ext-json": "*", "php": "^8.1", "psr/clock": "^1.0", - "symfony/clock": "^6.3 || ^7.0", + "symfony/clock": "^6.3.12 || ^7.0 || ^8.0", "symfony/polyfill-mbstring": "^1.0", - "symfony/translation": "^4.4.18 || ^5.2.1|| ^6.0 || ^7.0" + "symfony/translation": "^4.4.18 || ^5.2.1 || ^6.0 || ^7.0 || ^8.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -3145,24 +3450,19 @@ "require-dev": { "doctrine/dbal": "^3.6.3 || ^4.0", "doctrine/orm": "^2.15.2 || ^3.0", - "friendsofphp/php-cs-fixer": "^3.57.2", + "friendsofphp/php-cs-fixer": "^v3.87.1", "kylekatarnls/multi-tester": "^2.5.3", - "ondrejmirtes/better-reflection": "^6.25.0.4", "phpmd/phpmd": "^2.15.0", - "phpstan/extension-installer": "^1.3.1", - "phpstan/phpstan": "^1.11.2", - "phpunit/phpunit": "^10.5.20", - "squizlabs/php_codesniffer": "^3.9.0" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.22", + "phpunit/phpunit": "^10.5.53", + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" }, "bin": [ "bin/carbon" ], "type": "library", "extra": { - "branch-alias": { - "dev-master": "3.x-dev", - "dev-2.x": "2.x-dev" - }, "laravel": { "providers": [ "Carbon\\Laravel\\ServiceProvider" @@ -3172,6 +3472,10 @@ "includes": [ "extension.neon" ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" } }, "autoload": { @@ -3195,16 +3499,16 @@ } ], "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "https://carbon.nesbot.com", + "homepage": "https://carbonphp.github.io/carbon/", "keywords": [ "date", "datetime", "time" ], "support": { - "docs": "https://carbon.nesbot.com/docs", - "issues": "https://github.com/briannesbitt/Carbon/issues", - "source": "https://github.com/briannesbitt/Carbon" + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", + "issues": "https://github.com/CarbonPHP/carbon/issues", + "source": "https://github.com/CarbonPHP/carbon" }, "funding": [ { @@ -3220,29 +3524,31 @@ "type": "tidelift" } ], - "time": "2024-11-07T17:46:48+00:00" + "time": "2026-04-07T09:57:54+00:00" }, { "name": "nette/schema", - "version": "v1.3.2", + "version": "v1.3.5", "source": { "type": "git", "url": "https://github.com/nette/schema.git", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d" + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/schema/zipball/da801d52f0354f70a638673c4a0f04e16529431d", - "reference": "da801d52f0354f70a638673c4a0f04e16529431d", + "url": "https://api.github.com/repos/nette/schema/zipball/f0ab1a3cda782dbc5da270d28545236aa80c4002", + "reference": "f0ab1a3cda782dbc5da270d28545236aa80c4002", "shasum": "" }, "require": { "nette/utils": "^4.0", - "php": "8.1 - 8.4" + "php": "8.1 - 8.5" }, "require-dev": { - "nette/tester": "^2.5.2", - "phpstan/phpstan-nette": "^1.0", + "nette/phpstan-rules": "^1.0", + "nette/tester": "^2.6", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1.39@stable", "tracy/tracy": "^2.8" }, "type": "library", @@ -3252,6 +3558,9 @@ } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -3280,35 +3589,37 @@ ], "support": { "issues": "https://github.com/nette/schema/issues", - "source": "https://github.com/nette/schema/tree/v1.3.2" + "source": "https://github.com/nette/schema/tree/v1.3.5" }, - "time": "2024-10-06T23:10:23+00:00" + "time": "2026-02-23T03:47:12+00:00" }, { "name": "nette/utils", - "version": "v4.0.5", + "version": "v4.1.3", "source": { "type": "git", "url": "https://github.com/nette/utils.git", - "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96" + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", - "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96", + "url": "https://api.github.com/repos/nette/utils/zipball/bb3ea637e3d131d72acc033cfc2746ee893349fe", + "reference": "bb3ea637e3d131d72acc033cfc2746ee893349fe", "shasum": "" }, "require": { - "php": "8.0 - 8.4" + "php": "8.2 - 8.5" }, "conflict": { "nette/finder": "<3", "nette/schema": "<1.2.2" }, "require-dev": { - "jetbrains/phpstorm-attributes": "dev-master", + "jetbrains/phpstorm-attributes": "^1.2", + "nette/phpstan-rules": "^1.0", "nette/tester": "^2.5", - "phpstan/phpstan": "^1.0", + "phpstan/extension-installer": "^1.4@stable", + "phpstan/phpstan": "^2.1@stable", "tracy/tracy": "^2.9" }, "suggest": { @@ -3322,10 +3633,13 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-master": "4.1-dev" } }, "autoload": { + "psr-4": { + "Nette\\": "src" + }, "classmap": [ "src/" ] @@ -3366,22 +3680,22 @@ ], "support": { "issues": "https://github.com/nette/utils/issues", - "source": "https://github.com/nette/utils/tree/v4.0.5" + "source": "https://github.com/nette/utils/tree/v4.1.3" }, - "time": "2024-08-07T15:39:19+00:00" + "time": "2026-02-13T03:05:33+00:00" }, { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { @@ -3400,7 +3714,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -3424,35 +3738,35 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "nino/laravel-nextcloud-fs", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/treyssatvincent/laravel-nextcloud-fs.git", - "reference": "175ab3b266adc13985148b22cbde286f16c541dd" + "reference": "382907fb7a8cd9e3b5a6cc4fb2f1ad6b87545e20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/treyssatvincent/laravel-nextcloud-fs/zipball/175ab3b266adc13985148b22cbde286f16c541dd", - "reference": "175ab3b266adc13985148b22cbde286f16c541dd", + "url": "https://api.github.com/repos/treyssatvincent/laravel-nextcloud-fs/zipball/382907fb7a8cd9e3b5a6cc4fb2f1ad6b87545e20", + "reference": "382907fb7a8cd9e3b5a6cc4fb2f1ad6b87545e20", "shasum": "" }, "require": { - "illuminate/filesystem": "^10.38 || ^11.21.0", + "illuminate/filesystem": "^10.38 || ^11.21 || ^12.0", "league/flysystem-webdav": "^3.28", "php": "^8.2", "sabre/uri": ">=2.2.2" }, "require-dev": { "friendsofphp/php-cs-fixer": "^3.62", - "orchestra/testbench": "^8.18 || ^9.3", + "orchestra/testbench": "^9.3 || ^10.1", "phpstan/phpstan": "^1.11", - "phpunit/phpunit": "^10.1 || ^9.6" + "phpunit/phpunit": "^11.5 || ^10.1 || ^9.6" }, "type": "library", "extra": { @@ -3481,35 +3795,35 @@ "issues": "https://github.com/treyssatvincent/laravel-nextcloud-fs/issues", "source": "https://github.com/treyssatvincent/laravel-nextcloud-fs" }, - "time": "2024-08-25T13:02:17+00:00" + "time": "2025-03-16T11:24:21+00:00" }, { "name": "nunomaduro/termwind", - "version": "v2.3.0", + "version": "v2.4.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda" + "reference": "712a31b768f5daea284c2169a7d227031001b9a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda", - "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/712a31b768f5daea284c2169a7d227031001b9a8", + "reference": "712a31b768f5daea284c2169a7d227031001b9a8", "shasum": "" }, "require": { "ext-mbstring": "*", "php": "^8.2", - "symfony/console": "^7.1.8" + "symfony/console": "^7.4.4 || ^8.0.4" }, "require-dev": { - "illuminate/console": "^11.33.2", - "laravel/pint": "^1.18.2", + "illuminate/console": "^11.47.0", + "laravel/pint": "^1.27.1", "mockery/mockery": "^1.6.12", - "pestphp/pest": "^2.36.0", - "phpstan/phpstan": "^1.12.11", - "phpstan/phpstan-strict-rules": "^1.6.1", - "symfony/var-dumper": "^7.1.8", + "pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.3.2", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.5 || ^8.0.4", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -3541,7 +3855,7 @@ "email": "enunomaduro@gmail.com" } ], - "description": "Its like Tailwind CSS, but for the console.", + "description": "It's like Tailwind CSS, but for the console.", "keywords": [ "cli", "console", @@ -3552,7 +3866,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.3.0" + "source": "https://github.com/nunomaduro/termwind/tree/v2.4.0" }, "funding": [ { @@ -3568,23 +3882,24 @@ "type": "github" } ], - "time": "2024-11-21T10:39:51+00:00" + "time": "2026-02-16T23:10:27+00:00" }, { "name": "phpoffice/phpspreadsheet", - "version": "3.6.0", + "version": "1.30.4", "source": { "type": "git", "url": "https://github.com/PHPOffice/PhpSpreadsheet.git", - "reference": "bce5db99872f9613121c3ad033c43318a3789396" + "reference": "02970383cc12e7bf0bc0707ea6e2e8ed23a7aec9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/bce5db99872f9613121c3ad033c43318a3789396", - "reference": "bce5db99872f9613121c3ad033c43318a3789396", + "url": "https://api.github.com/repos/PHPOffice/PhpSpreadsheet/zipball/02970383cc12e7bf0bc0707ea6e2e8ed23a7aec9", + "reference": "02970383cc12e7bf0bc0707ea6e2e8ed23a7aec9", "shasum": "" }, "require": { + "composer/pcre": "^1||^2||^3", "ext-ctype": "*", "ext-dom": "*", "ext-fileinfo": "*", @@ -3598,24 +3913,24 @@ "ext-xmlwriter": "*", "ext-zip": "*", "ext-zlib": "*", + "ezyang/htmlpurifier": "^4.15", "maennchen/zipstream-php": "^2.1 || ^3.0", "markbaker/complex": "^3.0", "markbaker/matrix": "^3.0", - "php": "^8.1", - "psr/http-client": "^1.0", - "psr/http-factory": "^1.0", + "php": ">=7.4.0 <8.5.0", "psr/simple-cache": "^1.0 || ^2.0 || ^3.0" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "dev-main", - "dompdf/dompdf": "^2.0 || ^3.0", + "doctrine/instantiator": "^1.5", + "dompdf/dompdf": "^1.0 || ^2.0 || ^3.0", "friendsofphp/php-cs-fixer": "^3.2", "mitoteam/jpgraph": "^10.3", "mpdf/mpdf": "^8.1.1", "phpcompatibility/php-compatibility": "^9.3", "phpstan/phpstan": "^1.1", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^10.5", + "phpunit/phpunit": "^8.5 || ^9.0", "squizlabs/php_codesniffer": "^3.7", "tecnickcom/tcpdf": "^6.5" }, @@ -3654,6 +3969,9 @@ }, { "name": "Adrien Crivelli" + }, + { + "name": "Owen Leibman" } ], "description": "PHPSpreadsheet - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", @@ -3670,22 +3988,22 @@ ], "support": { "issues": "https://github.com/PHPOffice/PhpSpreadsheet/issues", - "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/3.6.0" + "source": "https://github.com/PHPOffice/PhpSpreadsheet/tree/1.30.4" }, - "time": "2024-12-08T15:04:12+00:00" + "time": "2026-04-19T06:00:39+00:00" }, { "name": "phpoption/phpoption", - "version": "1.9.3", + "version": "1.9.5", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", - "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", "shasum": "" }, "require": { @@ -3693,7 +4011,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" + "phpunit/phpunit": "^8.5.44 || ^9.6.25 || ^10.5.53 || ^11.5.34" }, "type": "library", "extra": { @@ -3735,7 +4053,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" }, "funding": [ { @@ -3747,7 +4065,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:41:07+00:00" + "time": "2025-12-27T19:41:33+00:00" }, { "name": "psr/clock", @@ -4163,16 +4481,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.7", + "version": "v0.12.22", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c" + "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", - "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/3be75d5b9244936dd4ac62ade2bfb004d13acf0f", + "reference": "3be75d5b9244936dd4ac62ade2bfb004d13acf0f", "shasum": "" }, "require": { @@ -4180,18 +4498,19 @@ "ext-tokenizer": "*", "nikic/php-parser": "^5.0 || ^4.0", "php": "^8.0 || ^7.4", - "symfony/console": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", - "symfony/var-dumper": "^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" + "symfony/console": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4", + "symfony/var-dumper": "^8.0 || ^7.0 || ^6.0 || ^5.0 || ^4.0 || ^3.4" }, "conflict": { "symfony/console": "4.4.37 || 5.3.14 || 5.3.15 || 5.4.3 || 5.4.4 || 6.0.3 || 6.0.4" }, "require-dev": { - "bamarni/composer-bin-plugin": "^1.2" + "bamarni/composer-bin-plugin": "^1.2", + "composer/class-map-generator": "^1.6" }, "suggest": { + "composer/class-map-generator": "Improved tab completion performance with better class discovery.", "ext-pcntl": "Enabling the PCNTL extension makes PsySH a lot happier :)", - "ext-pdo-sqlite": "The doc command requires SQLite to work.", "ext-posix": "If you have PCNTL, you'll want the POSIX extension as well." }, "bin": [ @@ -4222,12 +4541,11 @@ "authors": [ { "name": "Justin Hileman", - "email": "justin@justinhileman.info", - "homepage": "http://justinhileman.com" + "email": "justin@justinhileman.info" } ], "description": "An interactive shell for modern PHP.", - "homepage": "http://psysh.org", + "homepage": "https://psysh.org", "keywords": [ "REPL", "console", @@ -4236,9 +4554,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.7" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.22" }, - "time": "2024-12-10T01:58:33+00:00" + "time": "2026-03-22T23:03:24+00:00" }, { "name": "ralouphie/getallheaders", @@ -4286,16 +4604,16 @@ }, { "name": "ramsey/collection", - "version": "2.0.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/ramsey/collection.git", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5" + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", - "reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5", + "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2", + "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2", "shasum": "" }, "require": { @@ -4303,25 +4621,22 @@ }, "require-dev": { "captainhook/plugin-composer": "^5.3", - "ergebnis/composer-normalize": "^2.28.3", - "fakerphp/faker": "^1.21", + "ergebnis/composer-normalize": "^2.45", + "fakerphp/faker": "^1.24", "hamcrest/hamcrest-php": "^2.0", - "jangregor/phpstan-prophecy": "^1.0", - "mockery/mockery": "^1.5", + "jangregor/phpstan-prophecy": "^2.1", + "mockery/mockery": "^1.6", "php-parallel-lint/php-console-highlighter": "^1.0", - "php-parallel-lint/php-parallel-lint": "^1.3", - "phpcsstandards/phpcsutils": "^1.0.0-rc1", - "phpspec/prophecy-phpunit": "^2.0", - "phpstan/extension-installer": "^1.2", - "phpstan/phpstan": "^1.9", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^9.5", - "psalm/plugin-mockery": "^1.1", - "psalm/plugin-phpunit": "^0.18.4", - "ramsey/coding-standard": "^2.0.3", - "ramsey/conventional-commits": "^1.3", - "vimeo/psalm": "^5.4" + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpspec/prophecy-phpunit": "^2.3", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^10.5", + "ramsey/coding-standard": "^2.3", + "ramsey/conventional-commits": "^1.6", + "roave/security-advisories": "dev-latest" }, "type": "library", "extra": { @@ -4359,37 +4674,26 @@ ], "support": { "issues": "https://github.com/ramsey/collection/issues", - "source": "https://github.com/ramsey/collection/tree/2.0.0" + "source": "https://github.com/ramsey/collection/tree/2.1.1" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/collection", - "type": "tidelift" - } - ], - "time": "2022-12-31T21:50:55+00:00" + "time": "2025-03-22T05:38:12+00:00" }, { "name": "ramsey/uuid", - "version": "4.7.6", + "version": "4.9.2", "source": { "type": "git", "url": "https://github.com/ramsey/uuid.git", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088" + "reference": "8429c78ca35a09f27565311b98101e2826affde0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ramsey/uuid/zipball/91039bc1faa45ba123c4328958e620d382ec7088", - "reference": "91039bc1faa45ba123c4328958e620d382ec7088", + "url": "https://api.github.com/repos/ramsey/uuid/zipball/8429c78ca35a09f27565311b98101e2826affde0", + "reference": "8429c78ca35a09f27565311b98101e2826affde0", "shasum": "" }, "require": { - "brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12", - "ext-json": "*", + "brick/math": "^0.8.16 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13 || ^0.14", "php": "^8.0", "ramsey/collection": "^1.2 || ^2.0" }, @@ -4397,26 +4701,23 @@ "rhumsaa/uuid": "self.version" }, "require-dev": { - "captainhook/captainhook": "^5.10", + "captainhook/captainhook": "^5.25", "captainhook/plugin-composer": "^5.3", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "doctrine/annotations": "^1.8", - "ergebnis/composer-normalize": "^2.15", - "mockery/mockery": "^1.3", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "ergebnis/composer-normalize": "^2.47", + "mockery/mockery": "^1.6", "paragonie/random-lib": "^2", - "php-mock/php-mock": "^2.2", - "php-mock/php-mock-mockery": "^1.3", - "php-parallel-lint/php-parallel-lint": "^1.1", - "phpbench/phpbench": "^1.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^8.5 || ^9", - "ramsey/composer-repl": "^1.4", - "slevomat/coding-standard": "^8.4", - "squizlabs/php_codesniffer": "^3.5", - "vimeo/psalm": "^4.9" + "php-mock/php-mock": "^2.6", + "php-mock/php-mock-mockery": "^1.5", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpbench/phpbench": "^1.2.14", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan": "^2.1", + "phpstan/phpstan-mockery": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^9.6", + "slevomat/coding-standard": "^8.18", + "squizlabs/php_codesniffer": "^3.13" }, "suggest": { "ext-bcmath": "Enables faster math with arbitrary-precision integers using BCMath.", @@ -4451,40 +4752,41 @@ ], "support": { "issues": "https://github.com/ramsey/uuid/issues", - "source": "https://github.com/ramsey/uuid/tree/4.7.6" + "source": "https://github.com/ramsey/uuid/tree/4.9.2" }, - "funding": [ - { - "url": "https://github.com/ramsey", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid", - "type": "tidelift" - } - ], - "time": "2024-04-27T21:32:50+00:00" + "time": "2025-12-14T04:43:48+00:00" }, { "name": "sabberworm/php-css-parser", - "version": "v8.7.0", + "version": "v9.3.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "f414ff953002a9b18e3a116f5e462c56f21237cf" + "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/f414ff953002a9b18e3a116f5e462c56f21237cf", - "reference": "f414ff953002a9b18e3a116f5e462c56f21237cf", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949", + "reference": "88dbd0f7f91abbfe4402d0a3071e9ff4d81ed949", "shasum": "" }, "require": { "ext-iconv": "*", - "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" + "php": "^7.2.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "thecodingmachine/safe": "^1.3 || ^2.5 || ^3.4" }, "require-dev": { - "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.40" + "php-parallel-lint/php-parallel-lint": "1.4.0", + "phpstan/extension-installer": "1.4.3", + "phpstan/phpstan": "1.12.32 || 2.1.32", + "phpstan/phpstan-phpunit": "1.4.2 || 2.0.8", + "phpstan/phpstan-strict-rules": "1.6.2 || 2.0.7", + "phpunit/phpunit": "8.5.52", + "rawr/phpunit-data-provider": "3.3.1", + "rector/rector": "1.2.10 || 2.2.8", + "rector/type-perfect": "1.0.0 || 2.1.0", + "squizlabs/php_codesniffer": "4.0.1", + "thecodingmachine/phpstan-safe-rule": "1.2.0 || 1.4.1" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -4492,10 +4794,14 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "9.0.x-dev" + "dev-main": "9.4.x-dev" } }, "autoload": { + "files": [ + "src/Rule/Rule.php", + "src/RuleSet/RuleContainer.php" + ], "psr-4": { "Sabberworm\\CSS\\": "src/" } @@ -4526,9 +4832,9 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.7.0" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v9.3.0" }, - "time": "2024-10-27T17:38:32+00:00" + "time": "2026-03-03T17:31:43+00:00" }, { "name": "sabre/dav", @@ -4615,23 +4921,23 @@ }, { "name": "sabre/event", - "version": "5.1.7", + "version": "5.1.8", "source": { "type": "git", "url": "https://github.com/sabre-io/event.git", - "reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2" + "reference": "1dd5f55421b0092006510264131a4d632d02d2c1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/event/zipball/86d57e305c272898ba3c28e9bd3d65d5464587c2", - "reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2", + "url": "https://api.github.com/repos/sabre-io/event/zipball/1dd5f55421b0092006510264131a4d632d02d2c1", + "reference": "1dd5f55421b0092006510264131a4d632d02d2c1", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.17.1||^3.63", + "friendsofphp/php-cs-fixer": "~2.17.1||^3.95", "phpstan/phpstan": "^0.12", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, @@ -4677,20 +4983,20 @@ "issues": "https://github.com/sabre-io/event/issues", "source": "https://github.com/fruux/sabre-event" }, - "time": "2024-08-27T11:23:05+00:00" + "time": "2026-04-27T04:19:36+00:00" }, { "name": "sabre/http", - "version": "5.1.12", + "version": "5.1.13", "source": { "type": "git", "url": "https://github.com/sabre-io/http.git", - "reference": "dedff73f3995578bc942fa4c8484190cac14f139" + "reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/http/zipball/dedff73f3995578bc942fa4c8484190cac14f139", - "reference": "dedff73f3995578bc942fa4c8484190cac14f139", + "url": "https://api.github.com/repos/sabre-io/http/zipball/7c2a14097d1a0de2347dcbdc91a02f38e338f4db", + "reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db", "shasum": "" }, "require": { @@ -4702,7 +5008,7 @@ "sabre/uri": "^2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.17.1||^3.63", + "friendsofphp/php-cs-fixer": "~2.17.1||3.63.2", "phpstan/phpstan": "^0.12", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, @@ -4740,7 +5046,7 @@ "issues": "https://github.com/sabre-io/http/issues", "source": "https://github.com/fruux/sabre-http" }, - "time": "2024-08-27T16:07:41+00:00" + "time": "2025-09-09T10:21:47+00:00" }, { "name": "sabre/uri", @@ -4804,16 +5110,16 @@ }, { "name": "sabre/vobject", - "version": "4.5.6", + "version": "4.5.8", "source": { "type": "git", "url": "https://github.com/sabre-io/vobject.git", - "reference": "900266bb3bd448a9f7f41f82344ad0aba237cb27" + "reference": "d554eb24d64232922e1eab5896cc2f84b3b9ffb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/vobject/zipball/900266bb3bd448a9f7f41f82344ad0aba237cb27", - "reference": "900266bb3bd448a9f7f41f82344ad0aba237cb27", + "url": "https://api.github.com/repos/sabre-io/vobject/zipball/d554eb24d64232922e1eab5896cc2f84b3b9ffb1", + "reference": "d554eb24d64232922e1eab5896cc2f84b3b9ffb1", "shasum": "" }, "require": { @@ -4823,7 +5129,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "~2.17.1", - "phpstan/phpstan": "^0.12 || ^1.11", + "phpstan/phpstan": "^0.12 || ^1.12 || ^2.0", "phpunit/php-invoker": "^2.0 || ^3.1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, @@ -4904,7 +5210,7 @@ "issues": "https://github.com/sabre-io/vobject/issues", "source": "https://github.com/fruux/sabre-vobject" }, - "time": "2024-10-14T11:53:54+00:00" + "time": "2026-01-12T10:45:19+00:00" }, { "name": "sabre/xml", @@ -5036,16 +5342,16 @@ }, { "name": "spatie/image", - "version": "3.7.4", + "version": "3.9.4", "source": { "type": "git", "url": "https://github.com/spatie/image.git", - "reference": "d72d1ae07f91a3c1230e064acd4fd8c334ab237b" + "reference": "6a322b5e9268e3903d4fb6e1ff08b7dcc3aa9429" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/image/zipball/d72d1ae07f91a3c1230e064acd4fd8c334ab237b", - "reference": "d72d1ae07f91a3c1230e064acd4fd8c334ab237b", + "url": "https://api.github.com/repos/spatie/image/zipball/6a322b5e9268e3903d4fb6e1ff08b7dcc3aa9429", + "reference": "6a322b5e9268e3903d4fb6e1ff08b7dcc3aa9429", "shasum": "" }, "require": { @@ -5055,18 +5361,18 @@ "php": "^8.2", "spatie/image-optimizer": "^1.7.5", "spatie/temporary-directory": "^2.2", - "symfony/process": "^6.4|^7.0" + "symfony/process": "^6.4|^7.0|^8.0" }, "require-dev": { "ext-gd": "*", "ext-imagick": "*", "laravel/sail": "^1.34", - "pestphp/pest": "^2.28", + "pestphp/pest": "^3.0|^4.0", "phpstan/phpstan": "^1.10.50", "spatie/pest-plugin-snapshots": "^2.1", "spatie/pixelmatch-php": "^1.0", "spatie/ray": "^1.40.1", - "symfony/var-dumper": "^6.4|7.0" + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5093,7 +5399,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/image/tree/3.7.4" + "source": "https://github.com/spatie/image/tree/3.9.4" }, "funding": [ { @@ -5105,32 +5411,32 @@ "type": "github" } ], - "time": "2024-10-07T09:03:34+00:00" + "time": "2026-03-13T14:23:45+00:00" }, { "name": "spatie/image-optimizer", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/spatie/image-optimizer.git", - "reference": "4fd22035e81d98fffced65a8c20d9ec4daa9671c" + "reference": "2ad9ac7c19501739183359ae64ea6c15869c23d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/4fd22035e81d98fffced65a8c20d9ec4daa9671c", - "reference": "4fd22035e81d98fffced65a8c20d9ec4daa9671c", + "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/2ad9ac7c19501739183359ae64ea6c15869c23d9", + "reference": "2ad9ac7c19501739183359ae64ea6c15869c23d9", "shasum": "" }, "require": { "ext-fileinfo": "*", "php": "^7.3|^8.0", "psr/log": "^1.0 | ^2.0 | ^3.0", - "symfony/process": "^4.2|^5.0|^6.0|^7.0" + "symfony/process": "^4.2|^5.0|^6.0|^7.0|^8.0" }, "require-dev": { - "pestphp/pest": "^1.21", - "phpunit/phpunit": "^8.5.21|^9.4.4", - "symfony/var-dumper": "^4.2|^5.0|^6.0|^7.0" + "pestphp/pest": "^1.21|^2.0|^3.0|^4.0", + "phpunit/phpunit": "^8.5.21|^9.4.4|^10.0|^11.0|^12.0", + "symfony/var-dumper": "^4.2|^5.0|^6.0|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5158,43 +5464,43 @@ ], "support": { "issues": "https://github.com/spatie/image-optimizer/issues", - "source": "https://github.com/spatie/image-optimizer/tree/1.8.0" + "source": "https://github.com/spatie/image-optimizer/tree/1.8.1" }, - "time": "2024-11-04T08:24:54+00:00" + "time": "2025-11-26T10:57:19+00:00" }, { "name": "spatie/laravel-html", - "version": "3.11.1", + "version": "3.13.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-html.git", - "reference": "167e5b8243103072155b562e5cc396c90a3c1055" + "reference": "0579bf41959b4c4068206ec9bf1fcd1b59d8fa25" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-html/zipball/167e5b8243103072155b562e5cc396c90a3c1055", - "reference": "167e5b8243103072155b562e5cc396c90a3c1055", + "url": "https://api.github.com/repos/spatie/laravel-html/zipball/0579bf41959b4c4068206ec9bf1fcd1b59d8fa25", + "reference": "0579bf41959b4c4068206ec9bf1fcd1b59d8fa25", "shasum": "" }, "require": { - "illuminate/http": "^10.0|^11.0", - "illuminate/support": "^10.0|^11.0", + "illuminate/http": "^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^10.0|^11.0|^12.0|^13.0", "php": "^8.2" }, "require-dev": { "mockery/mockery": "^1.3", - "orchestra/testbench": "^8.0|^9.0", - "pestphp/pest": "^2.34" + "orchestra/testbench": "^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.34|^3.7|^4.0" }, "type": "library", "extra": { "laravel": { - "providers": [ - "Spatie\\Html\\HtmlServiceProvider" - ], "aliases": { "Html": "Spatie\\Html\\Facades\\Html" - } + }, + "providers": [ + "Spatie\\Html\\HtmlServiceProvider" + ] } }, "autoload": { @@ -5230,7 +5536,7 @@ "spatie" ], "support": { - "source": "https://github.com/spatie/laravel-html/tree/3.11.1" + "source": "https://github.com/spatie/laravel-html/tree/3.13.0" }, "funding": [ { @@ -5238,44 +5544,44 @@ "type": "custom" } ], - "time": "2024-10-18T14:37:21+00:00" + "time": "2026-02-22T09:05:56+00:00" }, { "name": "spatie/laravel-menu", - "version": "4.2.0", + "version": "4.2.2", "source": { "type": "git", "url": "https://github.com/spatie/laravel-menu.git", - "reference": "3f169410393dff9f45127cab552451fbc6d79e21" + "reference": "ae3e543715679cd39384476db17828b948c310fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-menu/zipball/3f169410393dff9f45127cab552451fbc6d79e21", - "reference": "3f169410393dff9f45127cab552451fbc6d79e21", + "url": "https://api.github.com/repos/spatie/laravel-menu/zipball/ae3e543715679cd39384476db17828b948c310fa", + "reference": "ae3e543715679cd39384476db17828b948c310fa", "shasum": "" }, "require": { - "illuminate/auth": "^8.0|^9.0|^10.0|^11.0", - "illuminate/contracts": "^8.71|^9.0|^10.0|^11.0", - "illuminate/support": "^8.71|^9.0|^10.0|^11.0", + "illuminate/auth": "^8.0|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^8.71|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^8.71|^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^8.0", "spatie/menu": "^3.0" }, "require-dev": { "fakerphp/faker": "^1.9", - "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0", - "pestphp/pest": "^1.22|^2.34", - "phpunit/phpunit": "^9.4|^10.5" + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^1.22|^2.34|^3.7|^4.0", + "phpunit/phpunit": "^9.4|^10.5|^11.5.3|^12.5.12" }, "type": "library", "extra": { "laravel": { - "providers": [ - "Spatie\\Menu\\Laravel\\MenuServiceProvider" - ], "aliases": { "Menu": "Spatie\\Menu\\Laravel\\Facades\\Menu" - } + }, + "providers": [ + "Spatie\\Menu\\Laravel\\MenuServiceProvider" + ] } }, "autoload": { @@ -5303,7 +5609,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-menu/issues", - "source": "https://github.com/spatie/laravel-menu/tree/4.2.0" + "source": "https://github.com/spatie/laravel-menu/tree/4.2.2" }, "funding": [ { @@ -5311,34 +5617,35 @@ "type": "custom" } ], - "time": "2024-02-27T12:08:53+00:00" + "time": "2026-02-21T21:16:50+00:00" }, { "name": "spatie/laravel-permission", - "version": "6.10.1", + "version": "6.25.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-permission.git", - "reference": "8bb69d6d67387f7a00d93a2f5fab98860f06e704" + "reference": "d7d4cb0d58616722f1afc90e0484e4825155b9b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/8bb69d6d67387f7a00d93a2f5fab98860f06e704", - "reference": "8bb69d6d67387f7a00d93a2f5fab98860f06e704", + "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/d7d4cb0d58616722f1afc90e0484e4825155b9b3", + "reference": "d7d4cb0d58616722f1afc90e0484e4825155b9b3", "shasum": "" }, "require": { - "illuminate/auth": "^8.12|^9.0|^10.0|^11.0", - "illuminate/container": "^8.12|^9.0|^10.0|^11.0", - "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0", - "illuminate/database": "^8.12|^9.0|^10.0|^11.0", + "illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", + "illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0|^13.0", "php": "^8.0" }, "require-dev": { - "larastan/larastan": "^1.0|^2.0", - "laravel/passport": "^11.0|^12.0", - "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0", - "phpunit/phpunit": "^9.4|^10.1" + "laravel/passport": "^11.0|^12.0|^13.0", + "laravel/pint": "^1.0", + "orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0|^11.0", + "pestphp/pest": "^2.0|^3.0|^4.0", + "pestphp/pest-plugin-laravel": "^2.0|^3.0|^4.0" }, "type": "library", "extra": { @@ -5386,7 +5693,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-permission/issues", - "source": "https://github.com/spatie/laravel-permission/tree/6.10.1" + "source": "https://github.com/spatie/laravel-permission/tree/6.25.0" }, "funding": [ { @@ -5394,27 +5701,28 @@ "type": "github" } ], - "time": "2024-11-08T18:45:41+00:00" + "time": "2026-03-17T22:46:46+00:00" }, { "name": "spatie/macroable", - "version": "2.0.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/spatie/macroable.git", - "reference": "ec2c320f932e730607aff8052c44183cf3ecb072" + "reference": "2967f69810ba4df158391665c0850010b9d1507c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/macroable/zipball/ec2c320f932e730607aff8052c44183cf3ecb072", - "reference": "ec2c320f932e730607aff8052c44183cf3ecb072", + "url": "https://api.github.com/repos/spatie/macroable/zipball/2967f69810ba4df158391665c0850010b9d1507c", + "reference": "2967f69810ba4df158391665c0850010b9d1507c", "shasum": "" }, "require": { - "php": "^8.0" + "php": "^8.3" }, "require-dev": { - "phpunit/phpunit": "^8.0|^9.3" + "pestphp/pest": "^4", + "phpunit/phpunit": "^12" }, "type": "library", "autoload": { @@ -5442,22 +5750,22 @@ ], "support": { "issues": "https://github.com/spatie/macroable/issues", - "source": "https://github.com/spatie/macroable/tree/2.0.0" + "source": "https://github.com/spatie/macroable/tree/2.1.0" }, - "time": "2021-03-26T22:39:02+00:00" + "time": "2026-01-30T17:13:34+00:00" }, { "name": "spatie/menu", - "version": "3.2.0", + "version": "3.2.1", "source": { "type": "git", "url": "https://github.com/spatie/menu.git", - "reference": "4241c69a7380c7c2c32677e0e995e698422a31aa" + "reference": "6b59c187e1800986bb354b849495e3cbbfff0f88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/menu/zipball/4241c69a7380c7c2c32677e0e995e698422a31aa", - "reference": "4241c69a7380c7c2c32677e0e995e698422a31aa", + "url": "https://api.github.com/repos/spatie/menu/zipball/6b59c187e1800986bb354b849495e3cbbfff0f88", + "reference": "6b59c187e1800986bb354b849495e3cbbfff0f88", "shasum": "" }, "require": { @@ -5497,7 +5805,7 @@ ], "support": { "issues": "https://github.com/spatie/menu/issues", - "source": "https://github.com/spatie/menu/tree/3.2.0" + "source": "https://github.com/spatie/menu/tree/3.2.1" }, "funding": [ { @@ -5509,20 +5817,20 @@ "type": "other" } ], - "time": "2023-04-12T10:02:12+00:00" + "time": "2025-06-13T07:09:57+00:00" }, { "name": "spatie/temporary-directory", - "version": "2.2.1", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/spatie/temporary-directory.git", - "reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a" + "reference": "662e481d6ec07ef29fd05010433428851a42cd07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a", - "reference": "76949fa18f8e1a7f663fd2eaa1d00e0bcea0752a", + "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/662e481d6ec07ef29fd05010433428851a42cd07", + "reference": "662e481d6ec07ef29fd05010433428851a42cd07", "shasum": "" }, "require": { @@ -5558,7 +5866,7 @@ ], "support": { "issues": "https://github.com/spatie/temporary-directory/issues", - "source": "https://github.com/spatie/temporary-directory/tree/2.2.1" + "source": "https://github.com/spatie/temporary-directory/tree/2.3.1" }, "funding": [ { @@ -5570,7 +5878,7 @@ "type": "github" } ], - "time": "2023-12-25T11:46:58+00:00" + "time": "2026-01-12T07:42:22+00:00" }, { "name": "spatie/url", @@ -5636,22 +5944,21 @@ }, { "name": "symfony/clock", - "version": "v7.2.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24" + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", - "reference": "b81435fbd6648ea425d1ee96a2d8e68f4ceacd24", + "url": "https://api.github.com/repos/symfony/clock/zipball/b55a638b189a6faa875e0ccdb00908fb87af95b3", + "reference": "b55a638b189a6faa875e0ccdb00908fb87af95b3", "shasum": "" }, "require": { - "php": ">=8.2", - "psr/clock": "^1.0", - "symfony/polyfill-php83": "^1.28" + "php": ">=8.4", + "psr/clock": "^1.0" }, "provide": { "psr/clock-implementation": "1.0" @@ -5690,7 +5997,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v7.2.0" + "source": "https://github.com/symfony/clock/tree/v8.0.8" }, "funding": [ { @@ -5701,32 +6008,37 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/console", - "version": "v7.2.1", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" + "reference": "d7d2b64a45a89d607865927b176fa51c33ddbb58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "url": "https://api.github.com/repos/symfony/console/zipball/d7d2b64a45a89d607865927b176fa51c33ddbb58", + "reference": "d7d2b64a45a89d607865927b176fa51c33ddbb58", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" + "symfony/string": "^7.2|^8.0" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -5740,16 +6052,16 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5783,7 +6095,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.1" + "source": "https://github.com/symfony/console/tree/v7.4.9" }, "funding": [ { @@ -5794,29 +6106,33 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-11T03:49:26+00:00" + "time": "2026-04-22T15:21:55+00:00" }, { "name": "symfony/css-selector", - "version": "v7.2.0", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2" + "reference": "3665cfade90565430909b906394c73c8739e57d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/601a5ce9aaad7bf10797e3663faefce9e26c24e2", - "reference": "601a5ce9aaad7bf10797e3663faefce9e26c24e2", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/3665cfade90565430909b906394c73c8739e57d0", + "reference": "3665cfade90565430909b906394c73c8739e57d0", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.4" }, "type": "library", "autoload": { @@ -5848,7 +6164,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v7.2.0" + "source": "https://github.com/symfony/css-selector/tree/v8.0.9" }, "funding": [ { @@ -5859,25 +6175,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { @@ -5885,12 +6205,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -5915,7 +6235,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -5926,40 +6246,47 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/error-handler", - "version": "v7.2.1", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "6150b89186573046167796fa5f3f76601d5145f8" + "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/6150b89186573046167796fa5f3f76601d5145f8", - "reference": "6150b89186573046167796fa5f3f76601d5145f8", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", + "reference": "8dd79d8af777ee6cba2fd4d98da6ffb839f3c0fa", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/polyfill-php85": "^1.32", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "conflict": { "symfony/deprecation-contracts": "<2.5", "symfony/http-kernel": "<6.4" }, "require-dev": { + "symfony/console": "^6.4|^7.0|^8.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0" + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/webpack-encore-bundle": "^1.0|^2.0" }, "bin": [ "Resources/bin/patch-type-declarations" @@ -5990,7 +6317,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.2.1" + "source": "https://github.com/symfony/error-handler/tree/v7.4.8" }, "funding": [ { @@ -6001,33 +6328,37 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-07T08:50:44+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.2.0", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", - "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0c3c1a17604c4dbbec4b93fe162c538482096e1f", + "reference": "0c3c1a17604c4dbbec4b93fe162c538482096e1f", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/security-http": "<7.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -6036,13 +6367,14 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/error-handler": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -6070,7 +6402,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.2.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v8.0.9" }, "funding": [ { @@ -6081,25 +6413,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-04-18T13:51:42+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", "shasum": "" }, "require": { @@ -6108,12 +6444,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -6146,7 +6482,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" }, "funding": [ { @@ -6157,32 +6493,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/finder", - "version": "v7.2.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49" + "reference": "e0be088d22278583a82da281886e8c3592fbf149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/6de263e5868b9a137602dd1e33e4d48bfae99c49", - "reference": "6de263e5868b9a137602dd1e33e4d48bfae99c49", + "url": "https://api.github.com/repos/symfony/finder/zipball/e0be088d22278583a82da281886e8c3592fbf149", + "reference": "e0be088d22278583a82da281886e8c3592fbf149", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6210,7 +6550,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.2.0" + "source": "https://github.com/symfony/finder/tree/v7.4.8" }, "funding": [ { @@ -6221,32 +6561,35 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-23T06:56:12+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.2.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "e88a66c3997859532bc2ddd6dd8f35aba2711744" + "reference": "9381209597ec66c25be154cbf2289076e64d1eab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e88a66c3997859532bc2ddd6dd8f35aba2711744", - "reference": "e88a66c3997859532bc2ddd6dd8f35aba2711744", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/9381209597ec66c25be154cbf2289076e64d1eab", + "reference": "9381209597ec66c25be154cbf2289076e64d1eab", "shasum": "" }, "require": { "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-mbstring": "~1.1", - "symfony/polyfill-php83": "^1.27" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "^1.1" }, "conflict": { "doctrine/dbal": "<3.6", @@ -6255,12 +6598,13 @@ "require-dev": { "doctrine/dbal": "^3.6|^4", "predis/predis": "^1.1|^2.0", - "symfony/cache": "^6.4.12|^7.1.5", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/mime": "^6.4|^7.0", - "symfony/rate-limiter": "^6.4|^7.0" + "symfony/cache": "^6.4.12|^7.1.5|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/mime": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6288,7 +6632,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.2.0" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.8" }, "funding": [ { @@ -6299,34 +6643,38 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-13T18:58:46+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.2.1", + "version": "v7.4.10", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "d8ae58eecae44c8e66833e76cc50a4ad3c002d97" + "reference": "23486f59234c6fd6e8f1bec97124f3829d686627" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d8ae58eecae44c8e66833e76cc50a4ad3c002d97", - "reference": "d8ae58eecae44c8e66833e76cc50a4ad3c002d97", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/23486f59234c6fd6e8f1bec97124f3829d686627", + "reference": "23486f59234c6fd6e8f1bec97124f3829d686627", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/error-handler": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^7.3|^8.0", + "symfony/http-foundation": "^7.4|^8.0", "symfony/polyfill-ctype": "^1.8" }, "conflict": { @@ -6336,6 +6684,7 @@ "symfony/console": "<6.4", "symfony/dependency-injection": "<6.4", "symfony/doctrine-bridge": "<6.4", + "symfony/flex": "<2.10", "symfony/form": "<6.4", "symfony/http-client": "<6.4", "symfony/http-client-contracts": "<2.5", @@ -6353,27 +6702,27 @@ }, "require-dev": { "psr/cache": "^1.0|^2.0|^3.0", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/clock": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/css-selector": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/dom-crawler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4.1|^7.0.1|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", "symfony/http-client-contracts": "^2.5|^3", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^7.1", - "symfony/routing": "^6.4|^7.0", - "symfony/serializer": "^7.1", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/translation": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^7.1|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/serializer": "^7.1|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", "symfony/translation-contracts": "^2.5|^3", - "symfony/uid": "^6.4|^7.0", - "symfony/validator": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0", - "symfony/var-exporter": "^6.4|^7.0", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "type": "library", @@ -6402,7 +6751,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.2.1" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.10" }, "funding": [ { @@ -6413,25 +6762,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-11T12:09:10+00:00" + "time": "2026-05-06T12:07:34+00:00" }, { "name": "symfony/mailer", - "version": "v7.2.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "e4d358702fb66e4c8a2af08e90e7271a62de39cc" + "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/e4d358702fb66e4c8a2af08e90e7271a62de39cc", - "reference": "e4d358702fb66e4c8a2af08e90e7271a62de39cc", + "url": "https://api.github.com/repos/symfony/mailer/zipball/f6ea532250b476bfc1b56699b388a1bdbf168f62", + "reference": "f6ea532250b476bfc1b56699b388a1bdbf168f62", "shasum": "" }, "require": { @@ -6439,8 +6792,8 @@ "php": ">=8.2", "psr/event-dispatcher": "^1", "psr/log": "^1|^2|^3", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/mime": "^7.2", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/mime": "^7.2|^8.0", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -6451,10 +6804,10 @@ "symfony/twig-bridge": "<6.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/twig-bridge": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -6482,7 +6835,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v7.2.0" + "source": "https://github.com/symfony/mailer/tree/v7.4.8" }, "funding": [ { @@ -6493,48 +6846,53 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-25T15:21:05+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/mime", - "version": "v7.2.1", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283" + "reference": "2d550c4758ba4c47519a6667c36553d535705b0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/7f9617fcf15cb61be30f8b252695ed5e2bfac283", - "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283", + "url": "https://api.github.com/repos/symfony/mime/zipball/2d550c4758ba4c47519a6667c36553d535705b0c", + "reference": "2d550c4758ba4c47519a6667c36553d535705b0c", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-intl-idn": "^1.10", "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "egulias/email-validator": "~3.0.0", - "phpdocumentor/reflection-docblock": "<3.2.2", - "phpdocumentor/type-resolver": "<1.4.0", + "phpdocumentor/reflection-docblock": "<5.2|>=7", + "phpdocumentor/type-resolver": "<1.5.1", "symfony/mailer": "<6.4", "symfony/serializer": "<6.4.3|>7.0,<7.0.3" }, "require-dev": { "egulias/email-validator": "^2.1.10|^3.1|^4", "league/html-to-markdown": "^5.0", - "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/property-access": "^6.4|^7.0", - "symfony/property-info": "^6.4|^7.0", - "symfony/serializer": "^6.4.3|^7.0.3" + "phpdocumentor/reflection-docblock": "^5.2|^6.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/serializer": "^6.4.3|^7.0.3|^8.0" }, "type": "library", "autoload": { @@ -6566,7 +6924,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.2.1" + "source": "https://github.com/symfony/mime/tree/v7.4.9" }, "funding": [ { @@ -6577,25 +6935,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-12-07T08:50:44+00:00" + "time": "2026-04-29T13:21:53+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -6645,7 +7007,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -6656,25 +7018,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", "shasum": "" }, "require": { @@ -6723,7 +7089,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" }, "funding": [ { @@ -6734,25 +7100,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-26T13:13:48+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", - "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", "shasum": "" }, "require": { @@ -6806,7 +7176,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.37.0" }, "funding": [ { @@ -6817,16 +7187,20 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-09-10T14:38:51+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -6887,7 +7261,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" }, "funding": [ { @@ -6898,6 +7272,10 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" @@ -6907,19 +7285,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -6967,7 +7346,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" }, "funding": [ { @@ -6978,25 +7357,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T17:25:58+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { @@ -7047,7 +7430,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -7058,25 +7441,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-php83", - "version": "v1.31.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491" + "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/2fb86d65e2d424369ad2905e83b236a8805ba491", - "reference": "2fb86d65e2d424369ad2905e83b236a8805ba491", + "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/3600c2cb22399e25bb226e4a135ce91eeb2a6149", + "reference": "3600c2cb22399e25bb226e4a135ce91eeb2a6149", "shasum": "" }, "require": { @@ -7123,7 +7510,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php83/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php83/tree/v1.37.0" }, "funding": [ { @@ -7134,25 +7521,109 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T17:25:58+00:00" }, { - "name": "symfony/polyfill-uuid", - "version": "v1.31.0", + "name": "symfony/polyfill-php85", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-uuid.git", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2" + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/21533be36c24be3f4b1669c4725c7d1d2bab4ae2", - "reference": "21533be36c24be3f4b1669c4725c7d1d2bab4ae2", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/fcfa4973a9917cef23f2e38774da74a2b7d115ee", + "reference": "fcfa4973a9917cef23f2e38774da74a2b7d115ee", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php85\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/tree/v1.37.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-04-26T13:10:57+00:00" + }, + { + "name": "symfony/polyfill-uuid", + "version": "v1.37.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-uuid.git", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-uuid/zipball/26dfec253c4cf3e51b541b52ddf7e42cb0908e94", + "reference": "26dfec253c4cf3e51b541b52ddf7e42cb0908e94", "shasum": "" }, "require": { @@ -7202,7 +7673,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/polyfill-uuid/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-uuid/tree/v1.37.0" }, "funding": [ { @@ -7213,25 +7684,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/process", - "version": "v7.2.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e" + "reference": "60f19cd3badc8de688421e21e4305eba50f8089a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", - "reference": "d34b22ba9390ec19d2dd966c40aa9e8462f27a7e", + "url": "https://api.github.com/repos/symfony/process/zipball/60f19cd3badc8de688421e21e4305eba50f8089a", + "reference": "60f19cd3badc8de688421e21e4305eba50f8089a", "shasum": "" }, "require": { @@ -7263,7 +7738,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.0" + "source": "https://github.com/symfony/process/tree/v7.4.8" }, "funding": [ { @@ -7274,45 +7749,49 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-06T14:24:19+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/psr-http-message-bridge", - "version": "v7.2.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/psr-http-message-bridge.git", - "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f" + "reference": "94facc221260c1d5f20e31ee43cd6c6a824b4a19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/03f2f72319e7acaf2a9f6fcbe30ef17eec51594f", - "reference": "03f2f72319e7acaf2a9f6fcbe30ef17eec51594f", + "url": "https://api.github.com/repos/symfony/psr-http-message-bridge/zipball/94facc221260c1d5f20e31ee43cd6c6a824b4a19", + "reference": "94facc221260c1d5f20e31ee43cd6c6a824b4a19", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/http-message": "^1.0|^2.0", - "symfony/http-foundation": "^6.4|^7.0" + "symfony/http-foundation": "^7.4|^8.0" }, "conflict": { - "php-http/discovery": "<1.15", - "symfony/http-kernel": "<6.4" + "php-http/discovery": "<1.15" }, "require-dev": { "nyholm/psr7": "^1.1", "php-http/discovery": "^1.15", "psr/log": "^1.1.4|^2|^3", - "symfony/browser-kit": "^6.4|^7.0", - "symfony/config": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/framework-bundle": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0" + "symfony/browser-kit": "^7.4|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/framework-bundle": "^7.4|^8.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/runtime": "^7.4|^8.0" }, "type": "symfony-bridge", "autoload": { @@ -7346,7 +7825,7 @@ "psr-7" ], "support": { - "source": "https://github.com/symfony/psr-http-message-bridge/tree/v7.2.0" + "source": "https://github.com/symfony/psr-http-message-bridge/tree/v8.0.8" }, "funding": [ { @@ -7357,25 +7836,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-26T08:57:56+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/routing", - "version": "v7.2.0", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "e10a2450fa957af6c448b9b93c9010a4e4c0725e" + "reference": "287771d8bc86eacb30678dd10eda6c64a859951f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/e10a2450fa957af6c448b9b93c9010a4e4c0725e", - "reference": "e10a2450fa957af6c448b9b93c9010a4e4c0725e", + "url": "https://api.github.com/repos/symfony/routing/zipball/287771d8bc86eacb30678dd10eda6c64a859951f", + "reference": "287771d8bc86eacb30678dd10eda6c64a859951f", "shasum": "" }, "require": { @@ -7389,11 +7872,11 @@ }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -7427,7 +7910,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v7.2.0" + "source": "https://github.com/symfony/routing/tree/v7.4.9" }, "funding": [ { @@ -7438,25 +7921,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-25T11:08:51+00:00" + "time": "2026-04-22T15:21:55+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { @@ -7469,12 +7956,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -7510,7 +7997,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -7521,44 +8008,47 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/string", - "version": "v7.2.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", - "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7597,7 +8087,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.2.0" + "source": "https://github.com/symfony/string/tree/v8.0.8" }, "funding": [ { @@ -7608,60 +8098,58 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-13T13:31:26+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/translation", - "version": "v7.2.0", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5" + "reference": "f63e9342e12646a57c91ef8a366a4f9d8e557b67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/dc89e16b44048ceecc879054e5b7f38326ab6cc5", - "reference": "dc89e16b44048ceecc879054e5b7f38326ab6cc5", + "url": "https://api.github.com/repos/symfony/translation/zipball/f63e9342e12646a57c91ef8a366a4f9d8e557b67", + "reference": "f63e9342e12646a57c91ef8a366a4f9d8e557b67", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" + "php": ">=8.4", + "symfony/polyfill-mbstring": "^1.0", + "symfony/translation-contracts": "^3.6.1" }, "conflict": { - "symfony/config": "<6.4", - "symfony/console": "<6.4", - "symfony/dependency-injection": "<6.4", + "nikic/php-parser": "<5.0", "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<6.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<6.4", - "symfony/yaml": "<6.4" + "symfony/service-contracts": "<2.5" }, "provide": { "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { - "nikic/php-parser": "^4.18|^5.0", + "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/console": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", + "symfony/config": "^7.4|^8.0", + "symfony/console": "^7.4|^8.0", + "symfony/dependency-injection": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/http-kernel": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^6.4|^7.0", + "symfony/routing": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -7692,7 +8180,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v7.2.0" + "source": "https://github.com/symfony/translation/tree/v8.0.10" }, "funding": [ { @@ -7703,25 +8191,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-12T20:47:56+00:00" + "time": "2026-05-06T11:30:54+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.5.1", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d", "shasum": "" }, "require": { @@ -7729,12 +8221,12 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -7770,7 +8262,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.0" }, "funding": [ { @@ -7781,25 +8273,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/uid", - "version": "v7.2.0", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "2d294d0c48df244c71c105a169d0190bfb080426" + "reference": "2676b524340abcfe4d6151ec698463cebafee439" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", - "reference": "2d294d0c48df244c71c105a169d0190bfb080426", + "url": "https://api.github.com/repos/symfony/uid/zipball/2676b524340abcfe4d6151ec698463cebafee439", + "reference": "2676b524340abcfe4d6151ec698463cebafee439", "shasum": "" }, "require": { @@ -7807,7 +8303,7 @@ "symfony/polyfill-uuid": "^1.15" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -7844,7 +8340,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.2.0" + "source": "https://github.com/symfony/uid/tree/v7.4.9" }, "funding": [ { @@ -7855,40 +8351,44 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2026-04-30T15:19:22+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.2.0", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "c6a22929407dec8765d6e2b6ff85b800b245879c" + "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/c6a22929407dec8765d6e2b6ff85b800b245879c", - "reference": "c6a22929407dec8765d6e2b6ff85b800b245879c", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/9510c3966f749a1d1ff0059e1eabef6cc621e7fd", + "reference": "9510c3966f749a1d1ff0059e1eabef6cc621e7fd", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/uid": "^6.4|^7.0|^8.0", "twig/twig": "^3.12" }, "bin": [ @@ -7927,7 +8427,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.2.0" + "source": "https://github.com/symfony/var-dumper/tree/v7.4.8" }, "funding": [ { @@ -7938,40 +8438,189 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-08T15:48:14+00:00" + "time": "2026-03-30T13:44:50+00:00" }, { - "name": "tijsverkoyen/css-to-inline-styles", - "version": "v2.2.7", + "name": "thecodingmachine/safe", + "version": "v3.4.0", "source": { "type": "git", - "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", - "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb" + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/83ee6f38df0a63106a9e4536e3060458b74ccedb", - "reference": "83ee6f38df0a63106a9e4536e3060458b74ccedb", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/705683a25bacf0d4860c7dea4d7947bfd09eea19", + "reference": "705683a25bacf0d4860c7dea4d7947bfd09eea19", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.4", + "phpstan/phpstan": "^2", + "phpunit/phpunit": "^10", + "squizlabs/php_codesniffer": "^3.2" + }, + "type": "library", + "autoload": { + "files": [ + "lib/special_cases.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/mysqli.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rnp.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v3.4.0" + }, + "funding": [ + { + "url": "https://github.com/OskarStark", + "type": "github" + }, + { + "url": "https://github.com/shish", + "type": "github" + }, + { + "url": "https://github.com/silasjoisten", + "type": "github" + }, + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2026-02-04T18:08:13+00:00" + }, + { + "name": "tijsverkoyen/css-to-inline-styles", + "version": "v2.4.0", + "source": { + "type": "git", + "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/f0292ccf0ec75843d65027214426b6b163b48b41", + "reference": "f0292ccf0ec75843d65027214426b6b163b48b41", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "php": "^5.5 || ^7.0 || ^8.0", - "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + "php": "^7.4 || ^8.0", + "symfony/css-selector": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5 || ^8.5.21 || ^9.5.10" + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpunit/phpunit": "^8.5.21 || ^9.5.10" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2.x-dev" + "dev-master": "2.x-dev" } }, "autoload": { @@ -7994,32 +8643,32 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "support": { "issues": "https://github.com/tijsverkoyen/CssToInlineStyles/issues", - "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.2.7" + "source": "https://github.com/tijsverkoyen/CssToInlineStyles/tree/v2.4.0" }, - "time": "2023-12-08T13:03:43+00:00" + "time": "2025-12-02T11:56:42+00:00" }, { "name": "vlucas/phpdotenv", - "version": "v5.6.1", + "version": "v5.6.3", "source": { "type": "git", "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2" + "reference": "955e7815d677a3eaa7075231212f2110983adecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/a59a13791077fe3d44f90e7133eb68e7d22eaff2", - "reference": "a59a13791077fe3d44f90e7133eb68e7d22eaff2", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "reference": "955e7815d677a3eaa7075231212f2110983adecc", "shasum": "" }, "require": { "ext-pcre": "*", - "graham-campbell/result-type": "^1.1.3", + "graham-campbell/result-type": "^1.1.4", "php": "^7.2.5 || ^8.0", - "phpoption/phpoption": "^1.9.3", - "symfony/polyfill-ctype": "^1.24", - "symfony/polyfill-mbstring": "^1.24", - "symfony/polyfill-php80": "^1.24" + "phpoption/phpoption": "^1.9.5", + "symfony/polyfill-ctype": "^1.26", + "symfony/polyfill-mbstring": "^1.26", + "symfony/polyfill-php80": "^1.26" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", @@ -8068,7 +8717,7 @@ ], "support": { "issues": "https://github.com/vlucas/phpdotenv/issues", - "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.1" + "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" }, "funding": [ { @@ -8080,27 +8729,27 @@ "type": "tidelift" } ], - "time": "2024-07-20T21:52:34+00:00" + "time": "2025-12-27T19:49:13+00:00" }, { "name": "voku/portable-ascii", - "version": "2.0.3", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + "reference": "8e1051fe39379367aecf014f41744ce7539a856f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/8e1051fe39379367aecf014f41744ce7539a856f", + "reference": "8e1051fe39379367aecf014f41744ce7539a856f", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + "phpunit/phpunit": "~8.5 || ~9.6 || ~10.5 || ~11.5" }, "suggest": { "ext-intl": "Use Intl for transliterator_transliterate() support" @@ -8130,7 +8779,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + "source": "https://github.com/voku/portable-ascii/tree/2.1.1" }, "funding": [ { @@ -8154,65 +8803,7 @@ "type": "tidelift" } ], - "time": "2024-11-21T01:49:47+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.11.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" - }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2026-04-26T05:33:54+00:00" }, { "name": "yajra/laravel-datatables-oracle", @@ -8254,16 +8845,16 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "11.x-dev" - }, "laravel": { - "providers": [ - "Yajra\\DataTables\\DataTablesServiceProvider" - ], "aliases": { "DataTables": "Yajra\\DataTables\\Facades\\DataTables" - } + }, + "providers": [ + "Yajra\\DataTables\\DataTablesServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "11.x-dev" } }, "autoload": { @@ -8370,16 +8961,16 @@ }, { "name": "filp/whoops", - "version": "2.16.0", + "version": "2.18.4", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "befcdc0e5dce67252aa6322d82424be928214fa2" + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/befcdc0e5dce67252aa6322d82424be928214fa2", - "reference": "befcdc0e5dce67252aa6322d82424be928214fa2", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", "shasum": "" }, "require": { @@ -8429,7 +9020,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.16.0" + "source": "https://github.com/filp/whoops/tree/2.18.4" }, "funding": [ { @@ -8437,24 +9028,24 @@ "type": "github" } ], - "time": "2024-09-25T12:00:00+00:00" + "time": "2025-08-08T12:00:00+00:00" }, { "name": "hamcrest/hamcrest-php", - "version": "v2.0.1", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", "shasum": "" }, "require": { - "php": "^5.3|^7.0|^8.0" + "php": "^7.4|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -8462,8 +9053,8 @@ "kodova/hamcrest-php": "*" }, "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -8486,43 +9077,44 @@ ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" }, - "time": "2020-07-09T08:09:16+00:00" + "time": "2025-04-30T06:54:44+00:00" }, { "name": "laravel/pail", - "version": "v1.2.1", + "version": "v1.2.6", "source": { "type": "git", "url": "https://github.com/laravel/pail.git", - "reference": "353ac12134b98e2e7c3333d916bd3e523931e583" + "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pail/zipball/353ac12134b98e2e7c3333d916bd3e523931e583", - "reference": "353ac12134b98e2e7c3333d916bd3e523931e583", + "url": "https://api.github.com/repos/laravel/pail/zipball/aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf", + "reference": "aa71a01c309e7f66bc2ec4fb1a59291b82eb4abf", "shasum": "" }, "require": { "ext-mbstring": "*", - "illuminate/console": "^10.24|^11.0", - "illuminate/contracts": "^10.24|^11.0", - "illuminate/log": "^10.24|^11.0", - "illuminate/process": "^10.24|^11.0", - "illuminate/support": "^10.24|^11.0", + "illuminate/console": "^10.24|^11.0|^12.0|^13.0", + "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0", + "illuminate/log": "^10.24|^11.0|^12.0|^13.0", + "illuminate/process": "^10.24|^11.0|^12.0|^13.0", + "illuminate/support": "^10.24|^11.0|^12.0|^13.0", "nunomaduro/termwind": "^1.15|^2.0", "php": "^8.2", - "symfony/console": "^6.0|^7.0" + "symfony/console": "^6.0|^7.0|^8.0" }, "require-dev": { - "laravel/framework": "^10.24|^11.0", + "laravel/framework": "^10.24|^11.0|^12.0|^13.0", "laravel/pint": "^1.13", - "orchestra/testbench-core": "^8.12|^9.0", - "pestphp/pest": "^2.20", - "pestphp/pest-plugin-type-coverage": "^2.3", - "phpstan/phpstan": "^1.10", - "symfony/var-dumper": "^6.3|^7.0" + "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0", + "pestphp/pest": "^2.20|^3.0|^4.0", + "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", + "phpstan/phpstan": "^1.12.27", + "symfony/var-dumper": "^6.3|^7.0|^8.0", + "symfony/yaml": "^6.3|^7.0|^8.0" }, "type": "library", "extra": { @@ -8557,6 +9149,7 @@ "description": "Easily delve into your Laravel application's log files directly from the command line.", "homepage": "https://github.com/laravel/pail", "keywords": [ + "dev", "laravel", "logs", "php", @@ -8566,20 +9159,20 @@ "issues": "https://github.com/laravel/pail/issues", "source": "https://github.com/laravel/pail" }, - "time": "2024-10-23T12:56:23+00:00" + "time": "2026-02-09T13:44:54+00:00" }, { "name": "laravel/pint", - "version": "v1.18.3", + "version": "v1.29.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "cef51821608239040ab841ad6e1c6ae502ae3026" + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/cef51821608239040ab841ad6e1c6ae502ae3026", - "reference": "cef51821608239040ab841ad6e1c6ae502ae3026", + "url": "https://api.github.com/repos/laravel/pint/zipball/0770e9b7fafd50d4586881d456d6eb41c9247a80", + "reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80", "shasum": "" }, "require": { @@ -8587,16 +9180,17 @@ "ext-mbstring": "*", "ext-tokenizer": "*", "ext-xml": "*", - "php": "^8.1.0" + "php": "^8.2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.65.0", - "illuminate/view": "^10.48.24", - "larastan/larastan": "^2.9.11", - "laravel-zero/framework": "^10.4.0", + "friendsofphp/php-cs-fixer": "^3.95.1", + "illuminate/view": "^12.56.0", + "larastan/larastan": "^3.9.6", + "laravel-zero/framework": "^12.1.0", "mockery/mockery": "^1.6.12", - "nunomaduro/termwind": "^1.17.0", - "pestphp/pest": "^2.36.0" + "nunomaduro/termwind": "^2.4.0", + "pestphp/pest": "^3.8.6", + "shipfastlabs/agent-detector": "^1.1.3" }, "bin": [ "builds/pint" @@ -8622,6 +9216,7 @@ "description": "An opinionated code formatter for PHP.", "homepage": "https://laravel.com", "keywords": [ + "dev", "format", "formatter", "lint", @@ -8632,33 +9227,33 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-11-26T15:34:00+00:00" + "time": "2026-04-20T15:26:14+00:00" }, { "name": "laravel/sail", - "version": "v1.39.1", + "version": "v1.58.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "1a3c7291bc88de983b66688919a4d298d68ddec7" + "reference": "2e5e968138ca52ed87d712449697a8364d73b466" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/1a3c7291bc88de983b66688919a4d298d68ddec7", - "reference": "1a3c7291bc88de983b66688919a4d298d68ddec7", + "url": "https://api.github.com/repos/laravel/sail/zipball/2e5e968138ca52ed87d712449697a8364d73b466", + "reference": "2e5e968138ca52ed87d712449697a8364d73b466", "shasum": "" }, "require": { - "illuminate/console": "^9.52.16|^10.0|^11.0", - "illuminate/contracts": "^9.52.16|^10.0|^11.0", - "illuminate/support": "^9.52.16|^10.0|^11.0", + "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", + "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", "php": "^8.0", - "symfony/console": "^6.0|^7.0", - "symfony/yaml": "^6.0|^7.0" + "symfony/console": "^6.0|^7.0|^8.0", + "symfony/yaml": "^6.0|^7.0|^8.0" }, "require-dev": { - "orchestra/testbench": "^7.0|^8.0|^9.0", - "phpstan/phpstan": "^1.10" + "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", + "phpstan/phpstan": "^2.0" }, "bin": [ "bin/sail" @@ -8695,7 +9290,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2024-11-27T15:42:28+00:00" + "time": "2026-04-27T13:38:34+00:00" }, { "name": "mockery/mockery", @@ -8782,16 +9377,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -8830,7 +9425,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -8838,42 +9433,40 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nunomaduro/collision", - "version": "v8.5.0", + "version": "v8.9.4", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "f5c101b929c958e849a633283adff296ed5f38f5" + "reference": "716af8f95a470e9094cfca09ed897b023be191a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", - "reference": "f5c101b929c958e849a633283adff296ed5f38f5", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/716af8f95a470e9094cfca09ed897b023be191a5", + "reference": "716af8f95a470e9094cfca09ed897b023be191a5", "shasum": "" }, "require": { - "filp/whoops": "^2.16.0", - "nunomaduro/termwind": "^2.1.0", + "filp/whoops": "^2.18.4", + "nunomaduro/termwind": "^2.4.0", "php": "^8.2.0", - "symfony/console": "^7.1.5" + "symfony/console": "^7.4.8 || ^8.0.8" }, "conflict": { - "laravel/framework": "<11.0.0 || >=12.0.0", - "phpunit/phpunit": "<10.5.1 || >=12.0.0" + "laravel/framework": "<11.48.0 || >=14.0.0", + "phpunit/phpunit": "<11.5.50 || >=14.0.0" }, "require-dev": { - "larastan/larastan": "^2.9.8", - "laravel/framework": "^11.28.0", - "laravel/pint": "^1.18.1", - "laravel/sail": "^1.36.0", - "laravel/sanctum": "^4.0.3", - "laravel/tinker": "^2.10.0", - "orchestra/testbench-core": "^9.5.3", - "pestphp/pest": "^2.36.0 || ^3.4.0", - "sebastian/environment": "^6.1.0 || ^7.2.0" + "brianium/paratest": "^7.8.5", + "larastan/larastan": "^3.9.6", + "laravel/framework": "^11.48.0 || ^12.56.0 || ^13.5.0", + "laravel/pint": "^1.29.1", + "orchestra/testbench-core": "^9.12.0 || ^10.12.1 || ^11.2.1", + "pestphp/pest": "^3.8.5 || ^4.4.3 || ^5.0.0", + "sebastian/environment": "^7.2.1 || ^8.0.4 || ^9.3.0" }, "type": "library", "extra": { @@ -8910,6 +9503,7 @@ "cli", "command-line", "console", + "dev", "error", "handling", "laravel", @@ -8935,7 +9529,7 @@ "type": "patreon" } ], - "time": "2024-10-15T16:06:32+00:00" + "time": "2026-04-21T14:04:20+00:00" }, { "name": "phar-io/manifest", @@ -9057,35 +9651,35 @@ }, { "name": "phpunit/php-code-coverage", - "version": "11.0.8", + "version": "11.0.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "418c59fd080954f8c4aa5631d9502ecda2387118" + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/418c59fd080954f8c4aa5631d9502ecda2387118", - "reference": "418c59fd080954f8c4aa5631d9502ecda2387118", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2c1ed04922802c15e1de5d7447b4856de949cf56", + "reference": "2c1ed04922802c15e1de5d7447b4856de949cf56", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^5.3.1", + "nikic/php-parser": "^5.7.0", "php": ">=8.2", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-text-template": "^4.0.1", "sebastian/code-unit-reverse-lookup": "^4.0.1", "sebastian/complexity": "^4.0.1", - "sebastian/environment": "^7.2.0", + "sebastian/environment": "^7.2.1", "sebastian/lines-of-code": "^3.0.1", "sebastian/version": "^5.0.2", - "theseer/tokenizer": "^1.2.3" + "theseer/tokenizer": "^1.3.1" }, "require-dev": { - "phpunit/phpunit": "^11.5.0" + "phpunit/phpunit": "^11.5.46" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -9123,40 +9717,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.8" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.12" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2024-12-11T12:34:27+00:00" + "time": "2025-12-24T07:01:01+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "5.1.0", + "version": "5.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/2f3a64888c814fc235386b7387dd5b5ed92ad903", + "reference": "2f3a64888c814fc235386b7387dd5b5ed92ad903", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -9184,15 +9790,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2024-08-27T05:02:59+00:00" + "time": "2026-02-02T13:52:54+00:00" }, { "name": "phpunit/php-invoker", @@ -9380,16 +9998,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.1", + "version": "11.5.55", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a" + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2b94d4f2450b9869fa64a46fd8a6a41997aef56a", - "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/adc7262fccc12de2b30f12a8aa0b33775d814f00", + "reference": "adc7262fccc12de2b30f12a8aa0b33775d814f00", "shasum": "" }, "require": { @@ -9399,24 +10017,25 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.7", - "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-code-coverage": "^11.0.12", + "phpunit/php-file-iterator": "^5.1.1", "phpunit/php-invoker": "^5.0.1", "phpunit/php-text-template": "^4.0.1", "phpunit/php-timer": "^7.0.1", "sebastian/cli-parser": "^3.0.2", - "sebastian/code-unit": "^3.0.1", - "sebastian/comparator": "^6.2.1", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.3", "sebastian/diff": "^6.0.2", - "sebastian/environment": "^7.2.0", - "sebastian/exporter": "^6.3.0", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.2", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", - "sebastian/type": "^5.1.0", + "sebastian/recursion-context": "^6.0.3", + "sebastian/type": "^5.1.3", "sebastian/version": "^5.0.2", "staabm/side-effects-detector": "^1.0.5" }, @@ -9461,7 +10080,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.1" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.55" }, "funding": [ { @@ -9472,12 +10091,20 @@ "url": "https://github.com/sebastianbergmann", "type": "github" }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", "type": "tidelift" } ], - "time": "2024-12-11T10:52:48+00:00" + "time": "2026-02-18T12:37:06+00:00" }, { "name": "sebastian/cli-parser", @@ -9538,16 +10165,16 @@ }, { "name": "sebastian/code-unit", - "version": "3.0.2", + "version": "3.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca" + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", - "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", "shasum": "" }, "require": { @@ -9583,7 +10210,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.2" + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" }, "funding": [ { @@ -9591,7 +10218,7 @@ "type": "github" } ], - "time": "2024-12-12T09:59:06+00:00" + "time": "2025-03-19T07:56:08+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -9651,16 +10278,16 @@ }, { "name": "sebastian/comparator", - "version": "6.2.1", + "version": "6.3.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739" + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/43d129d6a0f81c78bee378b46688293eb7ea3739", - "reference": "43d129d6a0f81c78bee378b46688293eb7ea3739", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", + "reference": "2c95e1e86cb8dd41beb8d502057d1081ccc8eca9", "shasum": "" }, "require": { @@ -9673,10 +10300,13 @@ "require-dev": { "phpunit/phpunit": "^11.4" }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" + }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.2-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -9716,15 +10346,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.2.1" + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2024-10-31T05:30:08+00:00" + "time": "2026-01-24T09:26:40+00:00" }, { "name": "sebastian/complexity", @@ -9853,23 +10495,23 @@ }, { "name": "sebastian/environment", - "version": "7.2.0", + "version": "7.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", - "reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "suggest": { "ext-posix": "*" @@ -9905,28 +10547,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", + "type": "tidelift" } ], - "time": "2024-07-03T04:54:44+00:00" + "time": "2025-05-21T11:55:47+00:00" }, { "name": "sebastian/exporter", - "version": "6.3.0", + "version": "6.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", - "reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { @@ -9940,7 +10594,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -9983,15 +10637,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" } ], - "time": "2024-12-05T09:17:50+00:00" + "time": "2025-09-24T06:12:51+00:00" }, { "name": "sebastian/global-state", @@ -10229,23 +10895,23 @@ }, { "name": "sebastian/recursion-context", - "version": "6.0.2", + "version": "6.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16" + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", - "reference": "694d156164372abbd149a4b85ccda2e4670c0e16", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { @@ -10281,28 +10947,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/recursion-context/issues", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2024-07-03T05:10:34+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { "name": "sebastian/type", - "version": "5.1.0", + "version": "5.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac" + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/461b9c5da241511a2a0e8f240814fb23ce5c0aac", - "reference": "461b9c5da241511a2a0e8f240814fb23ce5c0aac", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { @@ -10338,15 +11016,27 @@ "support": { "issues": "https://github.com/sebastianbergmann/type/issues", "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.0" + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2024-09-17T13:12:04+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { "name": "sebastian/version", @@ -10456,28 +11146,27 @@ }, { "name": "symfony/yaml", - "version": "v7.2.0", + "version": "v8.0.10", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "099581e99f557e9f16b43c5916c26380b54abb22" + "reference": "aa9ee60c41d9b20a2468c41ff0a32e2a7405ac05" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/099581e99f557e9f16b43c5916c26380b54abb22", - "reference": "099581e99f557e9f16b43c5916c26380b54abb22", + "url": "https://api.github.com/repos/symfony/yaml/zipball/aa9ee60c41d9b20a2468c41ff0a32e2a7405ac05", + "reference": "aa9ee60c41d9b20a2468c41ff0a32e2a7405ac05", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", + "php": ">=8.4", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<6.4" + "symfony/console": "<7.4" }, "require-dev": { - "symfony/console": "^6.4|^7.0" + "symfony/console": "^7.4|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -10508,7 +11197,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.2.0" + "source": "https://github.com/symfony/yaml/tree/v8.0.10" }, "funding": [ { @@ -10519,25 +11208,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-10-23T06:56:12+00:00" + "time": "2026-05-05T08:10:04+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, "require": { @@ -10566,7 +11259,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, "funding": [ { @@ -10574,7 +11267,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], @@ -10586,5 +11279,5 @@ "php": "^8.2" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/app.php b/config/app.php index 9ad90a7..48ca660 100644 --- a/config/app.php +++ b/config/app.php @@ -47,7 +47,7 @@ return [ | */ - 'timezone' => env('APP_TIMEZONE', 'UTC'), + 'timezone' => env('APP_TIMEZONE', 'Asia/Jakarta'), /* |-------------------------------------------------------------------------- diff --git a/config/database.php b/config/database.php index e28b152..64073d1 100644 --- a/config/database.php +++ b/config/database.php @@ -62,6 +62,20 @@ return [ ]) : [], ], + 'mysql_lama' => [ + 'driver' => 'mysql', + 'host' => env('DB_OLD_HOST', '127.0.0.1'), + 'port' => env('DB_PORT', '3306'), + 'database' => env('DB_OLD_DATABASE', 'forge'), + 'username' => env('DB_USERNAME', 'forge'), + 'password' => env('DB_PASSWORD', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'strict' => true, + 'engine' => null, +], + 'mariadb' => [ 'driver' => 'mariadb', 'url' => env('DB_URL'), @@ -78,8 +92,8 @@ return [ 'strict' => true, 'engine' => null, 'options' => extension_loaded('pdo_mysql') ? array_filter([ - PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), - ]) : [], + \Pdo\Mysql::ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'), +]) : [], ], 'pgsql' => [ diff --git a/database/migrations/2025_01_01_000000_create_attachment_form_table.php b/database/migrations/2025_01_01_000000_create_attachment_form_table.php index e8c11ce..93c32aa 100644 --- a/database/migrations/2025_01_01_000000_create_attachment_form_table.php +++ b/database/migrations/2025_01_01_000000_create_attachment_form_table.php @@ -6,23 +6,33 @@ use Illuminate\Support\Facades\Schema; return new class extends Migration { + /** + * Run the migrations. + */ public function up(): void { - Schema::create('attachment_form', function (Blueprint $table) { - $table->bigIncrements('id'); - $table->unsignedBigInteger('form_id'); - $table->string('table_name', 100)->comment('nama tabel form yang direferensikan'); - $table->string('file_category', 100)->nullable(); - $table->string('file_path', 255); - $table->timestamps(); - $table->softDeletes(); + // Pengecekan agar tidak error jika tabel sudah ada di database + if (!Schema::hasTable('attachment_form')) { + Schema::create('attachment_form', function (Blueprint $table) { + $table->bigIncrements('id'); + $table->unsignedBigInteger('form_id'); + $table->string('table_name', 100)->comment('nama tabel form yang direferensikan'); + $table->string('file_category', 100)->nullable(); + $table->string('file_path', 255); + $table->timestamps(); + $table->softDeletes(); - $table->index(['form_id', 'table_name'], 'idx_form_table'); - }); + // Menambahkan index untuk performa query + $table->index(['form_id', 'table_name'], 'idx_form_table'); + }); + } } + /** + * Reverse the migrations. + */ public function down(): void { Schema::dropIfExists('attachment_form'); } -}; +}; \ No newline at end of file diff --git a/database/migrations/2026_02_20_032520_add_region_id_to_budget_control_table.php b/database/migrations/2026_02_20_032520_add_region_id_to_budget_control_table.php new file mode 100644 index 0000000..afd7ddf --- /dev/null +++ b/database/migrations/2026_02_20_032520_add_region_id_to_budget_control_table.php @@ -0,0 +1,27 @@ +unsignedBigInteger('region_id')->nullable()->after('cabang_id'); + } + }); + } + + public function down(): void + { + Schema::table('budget_control', function (Blueprint $table) { + if (Schema::hasColumn('budget_control', 'region_id')) { + $table->dropColumn('region_id'); + } + }); + } +}; \ No newline at end of file diff --git a/database/migrations/2026_05_05_042142_add_transfer_fields_to_budget_controls_table.php b/database/migrations/2026_05_05_042142_add_transfer_fields_to_budget_controls_table.php new file mode 100644 index 0000000..e9c336e --- /dev/null +++ b/database/migrations/2026_05_05_042142_add_transfer_fields_to_budget_controls_table.php @@ -0,0 +1,35 @@ +string('transfer_proof')->after('remarks')->nullable(); + + // Menambahkan status transfer untuk approval Accounting + $table->enum('transfer_status', ['Pending', 'Approved', 'Rejected']) + ->default('Pending') + ->after('transfer_proof'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('budget_control', function (Blueprint $table) { + // Menghapus kolom jika migration di-rollback + $table->dropColumn(['transfer_proof', 'transfer_status']); + }); + } +}; \ No newline at end of file diff --git a/database/migrations/2026_05_05_051011_update_transfer_status_enum_on_budget_control_table.php b/database/migrations/2026_05_05_051011_update_transfer_status_enum_on_budget_control_table.php new file mode 100644 index 0000000..1a93c8d --- /dev/null +++ b/database/migrations/2026_05_05_051011_update_transfer_status_enum_on_budget_control_table.php @@ -0,0 +1,46 @@ +string('transfer_status')->change(); + }); + + // TAHAP 2: Update data lama ke status baru + // Kita tangani semua kemungkinan data agar tidak ada yang 'nyangkut' + DB::table('budget_control') + ->where('transfer_status', 'Approved') + ->update(['transfer_status' => 'Validated']); + + // Opsional: Jika ada data kosong/null, amankan ke 'Pending' + DB::table('budget_control') + ->whereNull('transfer_status') + ->orWhere('transfer_status', '') + ->update(['transfer_status' => 'Pending']); + + // TAHAP 3: Kembalikan kolom ke ENUM dengan definisi baru + Schema::table('budget_control', function (Blueprint $table) { + $table->enum('transfer_status', ['Pending', 'Waiting Approval', 'Validated', 'Rejected']) + ->default('Pending') + ->change(); + }); + } + + public function down(): void + { + // Rollback: Kembalikan ke definisi awal jika diperlukan + Schema::table('budget_control', function (Blueprint $table) { + $table->enum('transfer_status', ['Pending', 'Approved', 'Rejected']) + ->default('Pending') + ->change(); + }); + } +}; \ No newline at end of file diff --git a/database/migrations/2026_05_07_135152_add_region_and_cabang_to_users_table.php b/database/migrations/2026_05_07_135152_add_region_and_cabang_to_users_table.php new file mode 100644 index 0000000..6bd46f9 --- /dev/null +++ b/database/migrations/2026_05_07_135152_add_region_and_cabang_to_users_table.php @@ -0,0 +1,30 @@ +foreignId('region_id')->after('password')->nullable()->constrained('region'); + $table->foreignId('cabang_id')->after('region_id')->nullable()->constrained('cabang'); + }); +} + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + // + }); + } +}; diff --git a/database/migrations/2026_05_11_115253_create_transactions_table.php b/database/migrations/2026_05_11_115253_create_transactions_table.php new file mode 100644 index 0000000..a1f619d --- /dev/null +++ b/database/migrations/2026_05_11_115253_create_transactions_table.php @@ -0,0 +1,93 @@ +id(); + $table->foreignId('cabang_id')->constrained('cabang')->onDelete('cascade'); + $table->foreignId('budget_control_id')->nullable()->constrained('budget_control')->onDelete('set null'); + $table->date('transaction_date'); + $table->string('reference_number')->unique(); + $table->string('description'); + $table->decimal('amount', 15, 2); + $table->enum('type', ['DEBIT', 'CREDIT']); + $table->string('category')->default('General'); + $table->string('periode_month'); + $table->year('periode_year'); + $table->foreignId('created_by')->constrained('admins'); + $table->timestamps(); + $table->softDeletes(); + }); + return; + } + + // 2. JIKA TABEL SUDAH EXIST: Tambahkan kolom baru tanpa merusak / menimpa kolom cabang_id yang sudah ada + Schema::table('transactions', function (Blueprint $table) { + + // Pastikan cabang_id tidak dibuat ulang, kita hanya memastikan tipenya aman jika belum ada + if (!Schema::hasColumn('transactions', 'cabang_id')) { + $table->foreignId('cabang_id')->constrained('cabang')->onDelete('cascade'); + } + + // Tambahkan kolom pendukung lainnya secara aman jika belum eksis di database + if (!Schema::hasColumn('transactions', 'budget_control_id')) { + $table->foreignId('budget_control_id')->nullable()->after('cabang_id')->constrained('budget_control')->onDelete('set null'); + } + + if (!Schema::hasColumn('transactions', 'transaction_date')) { + $table->date('transaction_date')->nullable()->after('budget_control_id'); + } + + if (!Schema::hasColumn('transactions', 'reference_number')) { + $table->string('reference_number')->nullable()->after('transaction_date'); + } + + if (!Schema::hasColumn('transactions', 'description')) { + $table->string('description')->nullable()->after('reference_number'); + } + + if (!Schema::hasColumn('transactions', 'amount')) { + $table->decimal('amount', 15, 2)->default(0)->after('description'); + } + + if (!Schema::hasColumn('transactions', 'type')) { + $table->enum('type', ['DEBIT', 'CREDIT'])->default('CREDIT')->after('amount'); + } + + if (!Schema::hasColumn('transactions', 'category')) { + $table->string('category')->default('General')->after('type'); + } + + if (!Schema::hasColumn('transactions', 'periode_month')) { + $table->string('periode_month')->nullable()->after('category'); + } + + if (!Schema::hasColumn('transactions', 'periode_year')) { + $table->year('periode_year')->nullable()->after('periode_month'); + } + + if (!Schema::hasColumn('transactions', 'created_by')) { + // Buat nullable dulu agar baris data lama yang sudah exist tidak error saat migrasi berjalan + $table->foreignId('created_by')->nullable()->after('periode_year')->constrained('admins'); + } + + if (!Schema::hasColumn('transactions', 'deleted_at')) { + $table->softDeletes(); + } + }); + } + + public function down(): void + { + // Opsional: Hanya drop jika Anda benar-benar sedang di fase development murni + // Schema::dropIfExists('transactions'); + } +}; \ No newline at end of file diff --git a/database/migrations/2026_05_11_195438_add_details_to_form_entertainment_presentation_table.php b/database/migrations/2026_05_11_195438_add_details_to_form_entertainment_presentation_table.php new file mode 100644 index 0000000..6f9bcaf --- /dev/null +++ b/database/migrations/2026_05_11_195438_add_details_to_form_entertainment_presentation_table.php @@ -0,0 +1,27 @@ +string('jabatan')->nullable()->after('status'); + $table->string('nama_perusahaan')->nullable()->after('jabatan'); + $table->string('jenis_usaha')->nullable()->after('nama_perusahaan'); + }); +} + +public function down(): void +{ + Schema::table('form_entertainment_presentation', function (Blueprint $table) { + $table->dropColumn(['jabatan', 'nama_perusahaan', 'jenis_usaha']); + }); +} +}; diff --git a/database/migrations/2026_05_13_092826_add_rejection_metadata_to_all_forms.php b/database/migrations/2026_05_13_092826_add_rejection_metadata_to_all_forms.php new file mode 100644 index 0000000..69e9b5c --- /dev/null +++ b/database/migrations/2026_05_13_092826_add_rejection_metadata_to_all_forms.php @@ -0,0 +1,43 @@ +tables as $table) { + if (Schema::hasTable($table) && !Schema::hasColumn($table, 'rejected_by')) { + Schema::table($table, function (Blueprint $table) { + // Menambahkan kolom rejected_by sebagai foreign key ke tabel admins + $table->unsignedBigInteger('rejected_by')->nullable()->after('status'); + $table->timestamp('rejected_at')->nullable()->after('rejected_by'); + + $table->foreign('rejected_by')->references('id')->on('admins')->onDelete('set null'); + }); + } + } + } + + public function down(): void + { + foreach ($this->tables as $table) { + if (Schema::hasColumn($table, 'rejected_by')) { + Schema::table($table, function (Blueprint $table) { + $table->dropForeign([$table . '_rejected_by_foreign']); + $table->dropColumn(['rejected_by', 'rejected_at']); + }); + } + } + } +}; \ No newline at end of file diff --git a/database/migrations/2026_05_20_150655_add_kategori_id_to_dedicated_forms_table.php b/database/migrations/2026_05_20_150655_add_kategori_id_to_dedicated_forms_table.php new file mode 100644 index 0000000..53871ca --- /dev/null +++ b/database/migrations/2026_05_20_150655_add_kategori_id_to_dedicated_forms_table.php @@ -0,0 +1,43 @@ +tables as $table) { + if (Schema::hasTable($table) && !Schema::hasColumn($table, 'kategori_id')) { + Schema::table($table, function (Blueprint $bp) { + // Kita panggil nama tabel asli Anda 'kategori' di dalam fungsi constrained() + $bp->foreignId('kategori_id') + ->nullable() + ->after('user_id') + ->constrained('kategori') // <-- PERBAIKAN: Menembak tabel 'kategori' + ->onDelete('set null'); + }); + } + } + } + + public function down(): void + { + foreach ($this->tables as $table) { + if (Schema::hasColumn($table, 'kategori_id')) { + Schema::table($table, function (Blueprint $bp) use ($table) { + $bp->dropForeign($table . '_kategori_id_foreign'); + $bp->dropColumn('kategori_id'); + }); + } + } + } +}; \ No newline at end of file diff --git "a/e mengandung '25'n\";" "b/e mengandung '25'n\";" new file mode 100644 index 0000000..333a0b5 --- /dev/null +++ "b/e mengandung '25'n\";" @@ -0,0 +1,258 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^W WRAP search if no match found. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-M_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] + Use a lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n -N .... --line-numbers --LINE-NUMBERS + Don't use line numbers. + -o [_f_i_l_e] . --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t [_t_a_g] .. --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --incsearch + Search file as each pattern character is typed in. + --line-num-width=N + Set the width of the -N line number field to N characters. + --mouse + Enable mouse input. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --rscroll=C + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --status-col-width=N + Set the width of the -J status column to N characters. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=N + Each click of the mouse wheel moves N lines. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. diff --git "a/ed Tahun : GAGAL PARSING (\" . $e->getMessage() . \")n\";" "b/ed Tahun : GAGAL PARSING (\" . $e->getMessage() . \")n\";" new file mode 100644 index 0000000..333a0b5 --- /dev/null +++ "b/ed Tahun : GAGAL PARSING (\" . $e->getMessage() . \")n\";" @@ -0,0 +1,258 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^W WRAP search if no match found. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-M_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] + Use a lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n -N .... --line-numbers --LINE-NUMBERS + Don't use line numbers. + -o [_f_i_l_e] . --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t [_t_a_g] .. --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --incsearch + Search file as each pattern character is typed in. + --line-num-width=N + Set the width of the -N line number field to N characters. + --mouse + Enable mouse input. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --rscroll=C + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --status-col-width=N + Set the width of the -J status column to N characters. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=N + Each click of the mouse wheel moves N lines. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. diff --git a/elect('expense_number', DB::raw('count(*) as total')) b/elect('expense_number', DB::raw('count(*) as total')) new file mode 100644 index 0000000..91dc003 --- /dev/null +++ b/elect('expense_number', DB::raw('count(*) as total')) @@ -0,0 +1,79 @@ += Illuminate\Database\Query\Builder {#8371 + +connection: Illuminate\Database\MySqlConnection {#8375}, + +grammar: Illuminate\Database\Query\Grammars\MySqlGrammar {#8374}, + +processor: Illuminate\Database\Query\Processors\MySqlProcessor {#8373}, + +bindings: [ + "select" => [], + "from" => [], + "join" => [], + "where" => [], + "groupBy" => [], + "having" => [], + "order" => [], + "union" => [], + "unionOrder" => [], + ], + +aggregate: null, + +columns: null, + +distinct: false, + +from: "form_up_country", + +indexHint: null, + +joins: null, + +wheres: [], + +groups: null, + +havings: null, + +orders: null, + +limit: null, + +groupLimit: null, + +offset: null, + +unions: null, + +unionLimit: null, + +unionOffset: null, + +unionOrders: null, + +lock: null, + +beforeQueryCallbacks: [], + +operators: [ + "=", + "<", + ">", + "<=", + ">=", + "<>", + "!=", + "<=>", + "like", + "like binary", + "not like", + "ilike", + "&", + "|", + "^", + "<<", + ">>", + "&~", + "is", + "is not", + "rlike", + "not rlike", + "regexp", + "not regexp", + "~", + "~*", + "!~", + "!~*", + "similar to", + "not similar to", + "not ilike", + "~~*", + "!~~*", + ], + +bitwiseOperators: [ + "&", + "|", + "^", + "<<", + ">>", + "&~", + ], + +useWritePdo: false, + } diff --git "a/i Kolom Tgl : \" . $data->tanggal . \" (Type: \" . gettype($data->tanggal) . \")n\";" "b/i Kolom Tgl : \" . $data->tanggal . \" (Type: \" . gettype($data->tanggal) . \")n\";" new file mode 100644 index 0000000..333a0b5 --- /dev/null +++ "b/i Kolom Tgl : \" . $data->tanggal . \" (Type: \" . gettype($data->tanggal) . \")n\";" @@ -0,0 +1,258 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^W WRAP search if no match found. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-M_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k [_f_i_l_e] . --lesskey-file=[_f_i_l_e] + Use a lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n -N .... --line-numbers --LINE-NUMBERS + Don't use line numbers. + -o [_f_i_l_e] . --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] . --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p [_p_a_t_t_e_r_n] --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t [_t_a_g] .. --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --incsearch + Search file as each pattern character is typed in. + --line-num-width=N + Set the width of the -N line number field to N characters. + --mouse + Enable mouse input. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --rscroll=C + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --status-col-width=N + Set the width of the -J status column to N characters. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=N + Each click of the mouse wheel moves N lines. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2d2d495 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2700 @@ +{ + "name": "expensetia", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "autoprefixer": "^10.4.20", + "axios": "^1.7.4", + "concurrently": "^9.0.1", + "laravel-vite-plugin": "^1.0", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.13", + "vite": "^5.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.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@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/@rollup/rollup-android-arm-eabi": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/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/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.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "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.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.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.13.6", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", + "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz", + "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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/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/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "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": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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.30001777", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001777.tgz", + "integrity": "sha512-tmN+fJxroPndC74efCdp12j+0rk0RHwV5Jwa1zWaFVyw2ZxAuPeG8ZgWC3Wz7uSjT3qMRQ5XHZ4COgQmsCMJAQ==", + "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/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.2.1", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", + "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "4.1.2", + "rxjs": "7.8.2", + "shell-quote": "1.8.3", + "supports-color": "8.1.1", + "tree-kill": "1.2.2", + "yargs": "17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/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/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/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/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==", + "dev": true, + "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/electron-to-chromium": { + "version": "1.5.307", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.307.tgz", + "integrity": "sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==", + "dev": true, + "license": "ISC" + }, + "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/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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "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/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.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "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/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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-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-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/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/laravel-vite-plugin": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-1.3.0.tgz", + "integrity": "sha512-P5qyG56YbYxM8OuYmK2OkhcKe0AksNVJUjq9LUZ5tOekU9fBn9LujYyctI4t9XoLjuMvHJXXpCoPntY1oKltuA==", + "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/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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "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/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/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.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "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.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "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/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/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/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": "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/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.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/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.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "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/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/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/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/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.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "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.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", + "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.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "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/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "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.7", + "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 || ^5.0 || ^6.0", + "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/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.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", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "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/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/public/build/assets/app-BuG9aa18.js b/public/build/assets/app-BuG9aa18.js new file mode 100644 index 0000000..8bcc93c --- /dev/null +++ b/public/build/assets/app-BuG9aa18.js @@ -0,0 +1,6 @@ +function We(e,t){return function(){return e.apply(t,arguments)}}const{toString:mt}=Object.prototype,{getPrototypeOf:we}=Object,{iterator:re,toStringTag:Ke}=Symbol,se=(e=>t=>{const n=mt.call(t);return e[n]||(e[n]=n.slice(8,-1).toLowerCase())})(Object.create(null)),F=e=>(e=e.toLowerCase(),t=>se(t)===e),oe=e=>t=>typeof t===e,{isArray:M}=Array,H=oe("undefined");function J(e){return e!==null&&!H(e)&&e.constructor!==null&&!H(e.constructor)&&A(e.constructor.isBuffer)&&e.constructor.isBuffer(e)}const ve=F("ArrayBuffer");function yt(e){let t;return typeof ArrayBuffer<"u"&&ArrayBuffer.isView?t=ArrayBuffer.isView(e):t=e&&e.buffer&&ve(e.buffer),t}const bt=oe("string"),A=oe("function"),Xe=oe("number"),V=e=>e!==null&&typeof e=="object",wt=e=>e===!0||e===!1,Y=e=>{if(se(e)!=="object")return!1;const t=we(e);return(t===null||t===Object.prototype||Object.getPrototypeOf(t)===null)&&!(Ke in e)&&!(re in e)},Rt=e=>{if(!V(e)||J(e))return!1;try{return Object.keys(e).length===0&&Object.getPrototypeOf(e)===Object.prototype}catch{return!1}},Et=F("Date"),gt=F("File"),St=e=>!!(e&&typeof e.uri<"u"),Ot=e=>e&&typeof e.getParts<"u",Tt=F("Blob"),At=F("FileList"),Ct=e=>V(e)&&A(e.pipe);function xt(){return typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{}}const Ne=xt(),Pe=typeof Ne.FormData<"u"?Ne.FormData:void 0,_t=e=>{let t;return e&&(Pe&&e instanceof Pe||A(e.append)&&((t=se(e))==="formdata"||t==="object"&&A(e.toString)&&e.toString()==="[object FormData]"))},Nt=F("URLSearchParams"),[Pt,Ft,Ut,Lt]=["ReadableStream","Request","Response","Headers"].map(F),Dt=e=>e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"");function W(e,t,{allOwnKeys:n=!1}={}){if(e===null||typeof e>"u")return;let r,s;if(typeof e!="object"&&(e=[e]),M(e))for(r=0,s=e.length;r0;)if(s=n[r],t===s.toLowerCase())return s;return null}const k=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:global,Qe=e=>!H(e)&&e!==k;function he(){const{caseless:e,skipUndefined:t}=Qe(this)&&this||{},n={},r=(s,o)=>{if(o==="__proto__"||o==="constructor"||o==="prototype")return;const i=e&&Ge(n,o)||o;Y(n[i])&&Y(s)?n[i]=he(n[i],s):Y(s)?n[i]=he({},s):M(s)?n[i]=s.slice():(!t||!H(s))&&(n[i]=s)};for(let s=0,o=arguments.length;s(W(t,(s,o)=>{n&&A(s)?Object.defineProperty(e,o,{value:We(s,n),writable:!0,enumerable:!0,configurable:!0}):Object.defineProperty(e,o,{value:s,writable:!0,enumerable:!0,configurable:!0})},{allOwnKeys:r}),e),kt=e=>(e.charCodeAt(0)===65279&&(e=e.slice(1)),e),jt=(e,t,n,r)=>{e.prototype=Object.create(t.prototype,r),Object.defineProperty(e.prototype,"constructor",{value:e,writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(e,"super",{value:t.prototype}),n&&Object.assign(e.prototype,n)},qt=(e,t,n,r)=>{let s,o,i;const c={};if(t=t||{},e==null)return t;do{for(s=Object.getOwnPropertyNames(e),o=s.length;o-- >0;)i=s[o],(!r||r(i,e,t))&&!c[i]&&(t[i]=e[i],c[i]=!0);e=n!==!1&&we(e)}while(e&&(!n||n(e,t))&&e!==Object.prototype);return t},It=(e,t,n)=>{e=String(e),(n===void 0||n>e.length)&&(n=e.length),n-=t.length;const r=e.indexOf(t,n);return r!==-1&&r===n},Ht=e=>{if(!e)return null;if(M(e))return e;let t=e.length;if(!Xe(t))return null;const n=new Array(t);for(;t-- >0;)n[t]=e[t];return n},Mt=(e=>t=>e&&t instanceof e)(typeof Uint8Array<"u"&&we(Uint8Array)),$t=(e,t)=>{const r=(e&&e[re]).call(e);let s;for(;(s=r.next())&&!s.done;){const o=s.value;t.call(e,o[0],o[1])}},zt=(e,t)=>{let n;const r=[];for(;(n=e.exec(t))!==null;)r.push(n);return r},Jt=F("HTMLFormElement"),Vt=e=>e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,function(n,r,s){return r.toUpperCase()+s}),Fe=(({hasOwnProperty:e})=>(t,n)=>e.call(t,n))(Object.prototype),Wt=F("RegExp"),Ze=(e,t)=>{const n=Object.getOwnPropertyDescriptors(e),r={};W(n,(s,o)=>{let i;(i=t(s,o,e))!==!1&&(r[o]=i||s)}),Object.defineProperties(e,r)},Kt=e=>{Ze(e,(t,n)=>{if(A(e)&&["arguments","caller","callee"].indexOf(n)!==-1)return!1;const r=e[n];if(A(r)){if(t.enumerable=!1,"writable"in t){t.writable=!1;return}t.set||(t.set=()=>{throw Error("Can not rewrite read-only method '"+n+"'")})}})},vt=(e,t)=>{const n={},r=s=>{s.forEach(o=>{n[o]=!0})};return M(e)?r(e):r(String(e).split(t)),n},Xt=()=>{},Gt=(e,t)=>e!=null&&Number.isFinite(e=+e)?e:t;function Qt(e){return!!(e&&A(e.append)&&e[Ke]==="FormData"&&e[re])}const Zt=e=>{const t=new Array(10),n=(r,s)=>{if(V(r)){if(t.indexOf(r)>=0)return;if(J(r))return r;if(!("toJSON"in r)){t[s]=r;const o=M(r)?[]:{};return W(r,(i,c)=>{const p=n(i,s+1);!H(p)&&(o[c]=p)}),t[s]=void 0,o}}return r};return n(e,0)},Yt=F("AsyncFunction"),en=e=>e&&(V(e)||A(e))&&A(e.then)&&A(e.catch),Ye=((e,t)=>e?setImmediate:t?((n,r)=>(k.addEventListener("message",({source:s,data:o})=>{s===k&&o===n&&r.length&&r.shift()()},!1),s=>{r.push(s),k.postMessage(n,"*")}))(`axios@${Math.random()}`,[]):n=>setTimeout(n))(typeof setImmediate=="function",A(k.postMessage)),tn=typeof queueMicrotask<"u"?queueMicrotask.bind(k):typeof process<"u"&&process.nextTick||Ye,nn=e=>e!=null&&A(e[re]),a={isArray:M,isArrayBuffer:ve,isBuffer:J,isFormData:_t,isArrayBufferView:yt,isString:bt,isNumber:Xe,isBoolean:wt,isObject:V,isPlainObject:Y,isEmptyObject:Rt,isReadableStream:Pt,isRequest:Ft,isResponse:Ut,isHeaders:Lt,isUndefined:H,isDate:Et,isFile:gt,isReactNativeBlob:St,isReactNative:Ot,isBlob:Tt,isRegExp:Wt,isFunction:A,isStream:Ct,isURLSearchParams:Nt,isTypedArray:Mt,isFileList:At,forEach:W,merge:he,extend:Bt,trim:Dt,stripBOM:kt,inherits:jt,toFlatObject:qt,kindOf:se,kindOfTest:F,endsWith:It,toArray:Ht,forEachEntry:$t,matchAll:zt,isHTMLForm:Jt,hasOwnProperty:Fe,hasOwnProp:Fe,reduceDescriptors:Ze,freezeMethods:Kt,toObjectSet:vt,toCamelCase:Vt,noop:Xt,toFiniteNumber:Gt,findKey:Ge,global:k,isContextDefined:Qe,isSpecCompliantForm:Qt,toJSONObject:Zt,isAsyncFn:Yt,isThenable:en,setImmediate:Ye,asap:tn,isIterable:nn};let y=class et extends Error{static from(t,n,r,s,o,i){const c=new et(t.message,n||t.code,r,s,o);return c.cause=t,c.name=t.name,t.status!=null&&c.status==null&&(c.status=t.status),i&&Object.assign(c,i),c}constructor(t,n,r,s,o){super(t),Object.defineProperty(this,"message",{value:t,enumerable:!0,writable:!0,configurable:!0}),this.name="AxiosError",this.isAxiosError=!0,n&&(this.code=n),r&&(this.config=r),s&&(this.request=s),o&&(this.response=o,this.status=o.status)}toJSON(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:a.toJSONObject(this.config),code:this.code,status:this.status}}};y.ERR_BAD_OPTION_VALUE="ERR_BAD_OPTION_VALUE";y.ERR_BAD_OPTION="ERR_BAD_OPTION";y.ECONNABORTED="ECONNABORTED";y.ETIMEDOUT="ETIMEDOUT";y.ERR_NETWORK="ERR_NETWORK";y.ERR_FR_TOO_MANY_REDIRECTS="ERR_FR_TOO_MANY_REDIRECTS";y.ERR_DEPRECATED="ERR_DEPRECATED";y.ERR_BAD_RESPONSE="ERR_BAD_RESPONSE";y.ERR_BAD_REQUEST="ERR_BAD_REQUEST";y.ERR_CANCELED="ERR_CANCELED";y.ERR_NOT_SUPPORT="ERR_NOT_SUPPORT";y.ERR_INVALID_URL="ERR_INVALID_URL";const rn=null;function me(e){return a.isPlainObject(e)||a.isArray(e)}function tt(e){return a.endsWith(e,"[]")?e.slice(0,-2):e}function ue(e,t,n){return e?e.concat(t).map(function(s,o){return s=tt(s),!n&&o?"["+s+"]":s}).join(n?".":""):t}function sn(e){return a.isArray(e)&&!e.some(me)}const on=a.toFlatObject(a,{},null,function(t){return/^is[A-Z]/.test(t)});function ie(e,t,n){if(!a.isObject(e))throw new TypeError("target must be an object");t=t||new FormData,n=a.toFlatObject(n,{metaTokens:!0,dots:!1,indexes:!1},!1,function(m,d){return!a.isUndefined(d[m])});const r=n.metaTokens,s=n.visitor||l,o=n.dots,i=n.indexes,p=(n.Blob||typeof Blob<"u"&&Blob)&&a.isSpecCompliantForm(t);if(!a.isFunction(s))throw new TypeError("visitor must be a function");function f(u){if(u===null)return"";if(a.isDate(u))return u.toISOString();if(a.isBoolean(u))return u.toString();if(!p&&a.isBlob(u))throw new y("Blob is not supported. Use a Buffer instead.");return a.isArrayBuffer(u)||a.isTypedArray(u)?p&&typeof Blob=="function"?new Blob([u]):Buffer.from(u):u}function l(u,m,d){let R=u;if(a.isReactNative(t)&&a.isReactNativeBlob(u))return t.append(ue(d,m,o),f(u)),!1;if(u&&!d&&typeof u=="object"){if(a.endsWith(m,"{}"))m=r?m:m.slice(0,-2),u=JSON.stringify(u);else if(a.isArray(u)&&sn(u)||(a.isFileList(u)||a.endsWith(m,"[]"))&&(R=a.toArray(u)))return m=tt(m),R.forEach(function(E,O){!(a.isUndefined(E)||E===null)&&t.append(i===!0?ue([m],O,o):i===null?m:m+"[]",f(E))}),!1}return me(u)?!0:(t.append(ue(d,m,o),f(u)),!1)}const h=[],b=Object.assign(on,{defaultVisitor:l,convertValue:f,isVisitable:me});function g(u,m){if(!a.isUndefined(u)){if(h.indexOf(u)!==-1)throw Error("Circular reference detected in "+m.join("."));h.push(u),a.forEach(u,function(R,x){(!(a.isUndefined(R)||R===null)&&s.call(t,R,a.isString(x)?x.trim():x,m,b))===!0&&g(R,m?m.concat(x):[x])}),h.pop()}}if(!a.isObject(e))throw new TypeError("data must be an object");return g(e),t}function Ue(e){const t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,function(r){return t[r]})}function Re(e,t){this._pairs=[],e&&ie(e,this,t)}const nt=Re.prototype;nt.append=function(t,n){this._pairs.push([t,n])};nt.toString=function(t){const n=t?function(r){return t.call(this,r,Ue)}:Ue;return this._pairs.map(function(s){return n(s[0])+"="+n(s[1])},"").join("&")};function an(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+")}function rt(e,t,n){if(!t)return e;const r=n&&n.encode||an,s=a.isFunction(n)?{serialize:n}:n,o=s&&s.serialize;let i;if(o?i=o(t,s):i=a.isURLSearchParams(t)?t.toString():new Re(t,s).toString(r),i){const c=e.indexOf("#");c!==-1&&(e=e.slice(0,c)),e+=(e.indexOf("?")===-1?"?":"&")+i}return e}class Le{constructor(){this.handlers=[]}use(t,n,r){return this.handlers.push({fulfilled:t,rejected:n,synchronous:r?r.synchronous:!1,runWhen:r?r.runWhen:null}),this.handlers.length-1}eject(t){this.handlers[t]&&(this.handlers[t]=null)}clear(){this.handlers&&(this.handlers=[])}forEach(t){a.forEach(this.handlers,function(r){r!==null&&t(r)})}}const Ee={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1,legacyInterceptorReqResOrdering:!0},cn=typeof URLSearchParams<"u"?URLSearchParams:Re,ln=typeof FormData<"u"?FormData:null,un=typeof Blob<"u"?Blob:null,fn={isBrowser:!0,classes:{URLSearchParams:cn,FormData:ln,Blob:un},protocols:["http","https","file","blob","url","data"]},ge=typeof window<"u"&&typeof document<"u",ye=typeof navigator=="object"&&navigator||void 0,dn=ge&&(!ye||["ReactNative","NativeScript","NS"].indexOf(ye.product)<0),pn=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope&&typeof self.importScripts=="function",hn=ge&&window.location.href||"http://localhost",mn=Object.freeze(Object.defineProperty({__proto__:null,hasBrowserEnv:ge,hasStandardBrowserEnv:dn,hasStandardBrowserWebWorkerEnv:pn,navigator:ye,origin:hn},Symbol.toStringTag,{value:"Module"})),S={...mn,...fn};function yn(e,t){return ie(e,new S.classes.URLSearchParams,{visitor:function(n,r,s,o){return S.isNode&&a.isBuffer(n)?(this.append(r,n.toString("base64")),!1):o.defaultVisitor.apply(this,arguments)},...t})}function bn(e){return a.matchAll(/\w+|\[(\w*)]/g,e).map(t=>t[0]==="[]"?"":t[1]||t[0])}function wn(e){const t={},n=Object.keys(e);let r;const s=n.length;let o;for(r=0;r=n.length;return i=!i&&a.isArray(s)?s.length:i,p?(a.hasOwnProp(s,i)?s[i]=[s[i],r]:s[i]=r,!c):((!s[i]||!a.isObject(s[i]))&&(s[i]=[]),t(n,r,s[i],o)&&a.isArray(s[i])&&(s[i]=wn(s[i])),!c)}if(a.isFormData(e)&&a.isFunction(e.entries)){const n={};return a.forEachEntry(e,(r,s)=>{t(bn(r),s,n,0)}),n}return null}function Rn(e,t,n){if(a.isString(e))try{return(t||JSON.parse)(e),a.trim(e)}catch(r){if(r.name!=="SyntaxError")throw r}return(n||JSON.stringify)(e)}const K={transitional:Ee,adapter:["xhr","http","fetch"],transformRequest:[function(t,n){const r=n.getContentType()||"",s=r.indexOf("application/json")>-1,o=a.isObject(t);if(o&&a.isHTMLForm(t)&&(t=new FormData(t)),a.isFormData(t))return s?JSON.stringify(st(t)):t;if(a.isArrayBuffer(t)||a.isBuffer(t)||a.isStream(t)||a.isFile(t)||a.isBlob(t)||a.isReadableStream(t))return t;if(a.isArrayBufferView(t))return t.buffer;if(a.isURLSearchParams(t))return n.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),t.toString();let c;if(o){if(r.indexOf("application/x-www-form-urlencoded")>-1)return yn(t,this.formSerializer).toString();if((c=a.isFileList(t))||r.indexOf("multipart/form-data")>-1){const p=this.env&&this.env.FormData;return ie(c?{"files[]":t}:t,p&&new p,this.formSerializer)}}return o||s?(n.setContentType("application/json",!1),Rn(t)):t}],transformResponse:[function(t){const n=this.transitional||K.transitional,r=n&&n.forcedJSONParsing,s=this.responseType==="json";if(a.isResponse(t)||a.isReadableStream(t))return t;if(t&&a.isString(t)&&(r&&!this.responseType||s)){const i=!(n&&n.silentJSONParsing)&&s;try{return JSON.parse(t,this.parseReviver)}catch(c){if(i)throw c.name==="SyntaxError"?y.from(c,y.ERR_BAD_RESPONSE,this,null,this.response):c}}return t}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:S.classes.FormData,Blob:S.classes.Blob},validateStatus:function(t){return t>=200&&t<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};a.forEach(["delete","get","head","post","put","patch"],e=>{K.headers[e]={}});const En=a.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),gn=e=>{const t={};let n,r,s;return e&&e.split(` +`).forEach(function(i){s=i.indexOf(":"),n=i.substring(0,s).trim().toLowerCase(),r=i.substring(s+1).trim(),!(!n||t[n]&&En[n])&&(n==="set-cookie"?t[n]?t[n].push(r):t[n]=[r]:t[n]=t[n]?t[n]+", "+r:r)}),t},De=Symbol("internals");function z(e){return e&&String(e).trim().toLowerCase()}function ee(e){return e===!1||e==null?e:a.isArray(e)?e.map(ee):String(e)}function Sn(e){const t=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;let r;for(;r=n.exec(e);)t[r[1]]=r[2];return t}const On=e=>/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim());function fe(e,t,n,r,s){if(a.isFunction(r))return r.call(this,t,n);if(s&&(t=n),!!a.isString(t)){if(a.isString(r))return t.indexOf(r)!==-1;if(a.isRegExp(r))return r.test(t)}}function Tn(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(t,n,r)=>n.toUpperCase()+r)}function An(e,t){const n=a.toCamelCase(" "+t);["get","set","has"].forEach(r=>{Object.defineProperty(e,r+n,{value:function(s,o,i){return this[r].call(this,t,s,o,i)},configurable:!0})})}let C=class{constructor(t){t&&this.set(t)}set(t,n,r){const s=this;function o(c,p,f){const l=z(p);if(!l)throw new Error("header name must be a non-empty string");const h=a.findKey(s,l);(!h||s[h]===void 0||f===!0||f===void 0&&s[h]!==!1)&&(s[h||p]=ee(c))}const i=(c,p)=>a.forEach(c,(f,l)=>o(f,l,p));if(a.isPlainObject(t)||t instanceof this.constructor)i(t,n);else if(a.isString(t)&&(t=t.trim())&&!On(t))i(gn(t),n);else if(a.isObject(t)&&a.isIterable(t)){let c={},p,f;for(const l of t){if(!a.isArray(l))throw TypeError("Object iterator must return a key-value pair");c[f=l[0]]=(p=c[f])?a.isArray(p)?[...p,l[1]]:[p,l[1]]:l[1]}i(c,n)}else t!=null&&o(n,t,r);return this}get(t,n){if(t=z(t),t){const r=a.findKey(this,t);if(r){const s=this[r];if(!n)return s;if(n===!0)return Sn(s);if(a.isFunction(n))return n.call(this,s,r);if(a.isRegExp(n))return n.exec(s);throw new TypeError("parser must be boolean|regexp|function")}}}has(t,n){if(t=z(t),t){const r=a.findKey(this,t);return!!(r&&this[r]!==void 0&&(!n||fe(this,this[r],r,n)))}return!1}delete(t,n){const r=this;let s=!1;function o(i){if(i=z(i),i){const c=a.findKey(r,i);c&&(!n||fe(r,r[c],c,n))&&(delete r[c],s=!0)}}return a.isArray(t)?t.forEach(o):o(t),s}clear(t){const n=Object.keys(this);let r=n.length,s=!1;for(;r--;){const o=n[r];(!t||fe(this,this[o],o,t,!0))&&(delete this[o],s=!0)}return s}normalize(t){const n=this,r={};return a.forEach(this,(s,o)=>{const i=a.findKey(r,o);if(i){n[i]=ee(s),delete n[o];return}const c=t?Tn(o):String(o).trim();c!==o&&delete n[o],n[c]=ee(s),r[c]=!0}),this}concat(...t){return this.constructor.concat(this,...t)}toJSON(t){const n=Object.create(null);return a.forEach(this,(r,s)=>{r!=null&&r!==!1&&(n[s]=t&&a.isArray(r)?r.join(", "):r)}),n}[Symbol.iterator](){return Object.entries(this.toJSON())[Symbol.iterator]()}toString(){return Object.entries(this.toJSON()).map(([t,n])=>t+": "+n).join(` +`)}getSetCookie(){return this.get("set-cookie")||[]}get[Symbol.toStringTag](){return"AxiosHeaders"}static from(t){return t instanceof this?t:new this(t)}static concat(t,...n){const r=new this(t);return n.forEach(s=>r.set(s)),r}static accessor(t){const r=(this[De]=this[De]={accessors:{}}).accessors,s=this.prototype;function o(i){const c=z(i);r[c]||(An(s,i),r[c]=!0)}return a.isArray(t)?t.forEach(o):o(t),this}};C.accessor(["Content-Type","Content-Length","Accept","Accept-Encoding","User-Agent","Authorization"]);a.reduceDescriptors(C.prototype,({value:e},t)=>{let n=t[0].toUpperCase()+t.slice(1);return{get:()=>e,set(r){this[n]=r}}});a.freezeMethods(C);function de(e,t){const n=this||K,r=t||n,s=C.from(r.headers);let o=r.data;return a.forEach(e,function(c){o=c.call(n,o,s.normalize(),t?t.status:void 0)}),s.normalize(),o}function ot(e){return!!(e&&e.__CANCEL__)}let v=class extends y{constructor(t,n,r){super(t??"canceled",y.ERR_CANCELED,n,r),this.name="CanceledError",this.__CANCEL__=!0}};function it(e,t,n){const r=n.config.validateStatus;!n.status||!r||r(n.status)?e(n):t(new y("Request failed with status code "+n.status,[y.ERR_BAD_REQUEST,y.ERR_BAD_RESPONSE][Math.floor(n.status/100)-4],n.config,n.request,n))}function Cn(e){const t=/^([-+\w]{1,25})(:?\/\/|:)/.exec(e);return t&&t[1]||""}function xn(e,t){e=e||10;const n=new Array(e),r=new Array(e);let s=0,o=0,i;return t=t!==void 0?t:1e3,function(p){const f=Date.now(),l=r[o];i||(i=f),n[s]=p,r[s]=f;let h=o,b=0;for(;h!==s;)b+=n[h++],h=h%e;if(s=(s+1)%e,s===o&&(o=(o+1)%e),f-i{n=l,s=null,o&&(clearTimeout(o),o=null),e(...f)};return[(...f)=>{const l=Date.now(),h=l-n;h>=r?i(f,l):(s=f,o||(o=setTimeout(()=>{o=null,i(s)},r-h)))},()=>s&&i(s)]}const ne=(e,t,n=3)=>{let r=0;const s=xn(50,250);return _n(o=>{const i=o.loaded,c=o.lengthComputable?o.total:void 0,p=i-r,f=s(p),l=i<=c;r=i;const h={loaded:i,total:c,progress:c?i/c:void 0,bytes:p,rate:f||void 0,estimated:f&&c&&l?(c-i)/f:void 0,event:o,lengthComputable:c!=null,[t?"download":"upload"]:!0};e(h)},n)},Be=(e,t)=>{const n=e!=null;return[r=>t[0]({lengthComputable:n,total:e,loaded:r}),t[1]]},ke=e=>(...t)=>a.asap(()=>e(...t)),Nn=S.hasStandardBrowserEnv?((e,t)=>n=>(n=new URL(n,S.origin),e.protocol===n.protocol&&e.host===n.host&&(t||e.port===n.port)))(new URL(S.origin),S.navigator&&/(msie|trident)/i.test(S.navigator.userAgent)):()=>!0,Pn=S.hasStandardBrowserEnv?{write(e,t,n,r,s,o,i){if(typeof document>"u")return;const c=[`${e}=${encodeURIComponent(t)}`];a.isNumber(n)&&c.push(`expires=${new Date(n).toUTCString()}`),a.isString(r)&&c.push(`path=${r}`),a.isString(s)&&c.push(`domain=${s}`),o===!0&&c.push("secure"),a.isString(i)&&c.push(`SameSite=${i}`),document.cookie=c.join("; ")},read(e){if(typeof document>"u")return null;const t=document.cookie.match(new RegExp("(?:^|; )"+e+"=([^;]*)"));return t?decodeURIComponent(t[1]):null},remove(e){this.write(e,"",Date.now()-864e5,"/")}}:{write(){},read(){return null},remove(){}};function Fn(e){return typeof e!="string"?!1:/^([a-z][a-z\d+\-.]*:)?\/\//i.test(e)}function Un(e,t){return t?e.replace(/\/?\/$/,"")+"/"+t.replace(/^\/+/,""):e}function at(e,t,n){let r=!Fn(t);return e&&(r||n==!1)?Un(e,t):t}const je=e=>e instanceof C?{...e}:e;function q(e,t){t=t||{};const n={};function r(f,l,h,b){return a.isPlainObject(f)&&a.isPlainObject(l)?a.merge.call({caseless:b},f,l):a.isPlainObject(l)?a.merge({},l):a.isArray(l)?l.slice():l}function s(f,l,h,b){if(a.isUndefined(l)){if(!a.isUndefined(f))return r(void 0,f,h,b)}else return r(f,l,h,b)}function o(f,l){if(!a.isUndefined(l))return r(void 0,l)}function i(f,l){if(a.isUndefined(l)){if(!a.isUndefined(f))return r(void 0,f)}else return r(void 0,l)}function c(f,l,h){if(h in t)return r(f,l);if(h in e)return r(void 0,f)}const p={url:o,method:o,data:o,baseURL:i,transformRequest:i,transformResponse:i,paramsSerializer:i,timeout:i,timeoutMessage:i,withCredentials:i,withXSRFToken:i,adapter:i,responseType:i,xsrfCookieName:i,xsrfHeaderName:i,onUploadProgress:i,onDownloadProgress:i,decompress:i,maxContentLength:i,maxBodyLength:i,beforeRedirect:i,transport:i,httpAgent:i,httpsAgent:i,cancelToken:i,socketPath:i,responseEncoding:i,validateStatus:c,headers:(f,l,h)=>s(je(f),je(l),h,!0)};return a.forEach(Object.keys({...e,...t}),function(l){if(l==="__proto__"||l==="constructor"||l==="prototype")return;const h=a.hasOwnProp(p,l)?p[l]:s,b=h(e[l],t[l],l);a.isUndefined(b)&&h!==c||(n[l]=b)}),n}const ct=e=>{const t=q({},e);let{data:n,withXSRFToken:r,xsrfHeaderName:s,xsrfCookieName:o,headers:i,auth:c}=t;if(t.headers=i=C.from(i),t.url=rt(at(t.baseURL,t.url,t.allowAbsoluteUrls),e.params,e.paramsSerializer),c&&i.set("Authorization","Basic "+btoa((c.username||"")+":"+(c.password?unescape(encodeURIComponent(c.password)):""))),a.isFormData(n)){if(S.hasStandardBrowserEnv||S.hasStandardBrowserWebWorkerEnv)i.setContentType(void 0);else if(a.isFunction(n.getHeaders)){const p=n.getHeaders(),f=["content-type","content-length"];Object.entries(p).forEach(([l,h])=>{f.includes(l.toLowerCase())&&i.set(l,h)})}}if(S.hasStandardBrowserEnv&&(r&&a.isFunction(r)&&(r=r(t)),r||r!==!1&&Nn(t.url))){const p=s&&o&&Pn.read(o);p&&i.set(s,p)}return t},Ln=typeof XMLHttpRequest<"u",Dn=Ln&&function(e){return new Promise(function(n,r){const s=ct(e);let o=s.data;const i=C.from(s.headers).normalize();let{responseType:c,onUploadProgress:p,onDownloadProgress:f}=s,l,h,b,g,u;function m(){g&&g(),u&&u(),s.cancelToken&&s.cancelToken.unsubscribe(l),s.signal&&s.signal.removeEventListener("abort",l)}let d=new XMLHttpRequest;d.open(s.method.toUpperCase(),s.url,!0),d.timeout=s.timeout;function R(){if(!d)return;const E=C.from("getAllResponseHeaders"in d&&d.getAllResponseHeaders()),P={data:!c||c==="text"||c==="json"?d.responseText:d.response,status:d.status,statusText:d.statusText,headers:E,config:e,request:d};it(function(_){n(_),m()},function(_){r(_),m()},P),d=null}"onloadend"in d?d.onloadend=R:d.onreadystatechange=function(){!d||d.readyState!==4||d.status===0&&!(d.responseURL&&d.responseURL.indexOf("file:")===0)||setTimeout(R)},d.onabort=function(){d&&(r(new y("Request aborted",y.ECONNABORTED,e,d)),d=null)},d.onerror=function(O){const P=O&&O.message?O.message:"Network Error",D=new y(P,y.ERR_NETWORK,e,d);D.event=O||null,r(D),d=null},d.ontimeout=function(){let O=s.timeout?"timeout of "+s.timeout+"ms exceeded":"timeout exceeded";const P=s.transitional||Ee;s.timeoutErrorMessage&&(O=s.timeoutErrorMessage),r(new y(O,P.clarifyTimeoutError?y.ETIMEDOUT:y.ECONNABORTED,e,d)),d=null},o===void 0&&i.setContentType(null),"setRequestHeader"in d&&a.forEach(i.toJSON(),function(O,P){d.setRequestHeader(P,O)}),a.isUndefined(s.withCredentials)||(d.withCredentials=!!s.withCredentials),c&&c!=="json"&&(d.responseType=s.responseType),f&&([b,u]=ne(f,!0),d.addEventListener("progress",b)),p&&d.upload&&([h,g]=ne(p),d.upload.addEventListener("progress",h),d.upload.addEventListener("loadend",g)),(s.cancelToken||s.signal)&&(l=E=>{d&&(r(!E||E.type?new v(null,e,d):E),d.abort(),d=null)},s.cancelToken&&s.cancelToken.subscribe(l),s.signal&&(s.signal.aborted?l():s.signal.addEventListener("abort",l)));const x=Cn(s.url);if(x&&S.protocols.indexOf(x)===-1){r(new y("Unsupported protocol "+x+":",y.ERR_BAD_REQUEST,e));return}d.send(o||null)})},Bn=(e,t)=>{const{length:n}=e=e?e.filter(Boolean):[];if(t||n){let r=new AbortController,s;const o=function(f){if(!s){s=!0,c();const l=f instanceof Error?f:this.reason;r.abort(l instanceof y?l:new v(l instanceof Error?l.message:l))}};let i=t&&setTimeout(()=>{i=null,o(new y(`timeout of ${t}ms exceeded`,y.ETIMEDOUT))},t);const c=()=>{e&&(i&&clearTimeout(i),i=null,e.forEach(f=>{f.unsubscribe?f.unsubscribe(o):f.removeEventListener("abort",o)}),e=null)};e.forEach(f=>f.addEventListener("abort",o));const{signal:p}=r;return p.unsubscribe=()=>a.asap(c),p}},kn=function*(e,t){let n=e.byteLength;if(n{const s=jn(e,t);let o=0,i,c=p=>{i||(i=!0,r&&r(p))};return new ReadableStream({async pull(p){try{const{done:f,value:l}=await s.next();if(f){c(),p.close();return}let h=l.byteLength;if(n){let b=o+=h;n(b)}p.enqueue(new Uint8Array(l))}catch(f){throw c(f),f}},cancel(p){return c(p),s.return()}},{highWaterMark:2})},Ie=64*1024,{isFunction:Z}=a,In=(({Request:e,Response:t})=>({Request:e,Response:t}))(a.global),{ReadableStream:He,TextEncoder:Me}=a.global,$e=(e,...t)=>{try{return!!e(...t)}catch{return!1}},Hn=e=>{e=a.merge.call({skipUndefined:!0},In,e);const{fetch:t,Request:n,Response:r}=e,s=t?Z(t):typeof fetch=="function",o=Z(n),i=Z(r);if(!s)return!1;const c=s&&Z(He),p=s&&(typeof Me=="function"?(u=>m=>u.encode(m))(new Me):async u=>new Uint8Array(await new n(u).arrayBuffer())),f=o&&c&&$e(()=>{let u=!1;const m=new n(S.origin,{body:new He,method:"POST",get duplex(){return u=!0,"half"}}).headers.has("Content-Type");return u&&!m}),l=i&&c&&$e(()=>a.isReadableStream(new r("").body)),h={stream:l&&(u=>u.body)};s&&["text","arrayBuffer","blob","formData","stream"].forEach(u=>{!h[u]&&(h[u]=(m,d)=>{let R=m&&m[u];if(R)return R.call(m);throw new y(`Response type '${u}' is not supported`,y.ERR_NOT_SUPPORT,d)})});const b=async u=>{if(u==null)return 0;if(a.isBlob(u))return u.size;if(a.isSpecCompliantForm(u))return(await new n(S.origin,{method:"POST",body:u}).arrayBuffer()).byteLength;if(a.isArrayBufferView(u)||a.isArrayBuffer(u))return u.byteLength;if(a.isURLSearchParams(u)&&(u=u+""),a.isString(u))return(await p(u)).byteLength},g=async(u,m)=>{const d=a.toFiniteNumber(u.getContentLength());return d??b(m)};return async u=>{let{url:m,method:d,data:R,signal:x,cancelToken:E,timeout:O,onDownloadProgress:P,onUploadProgress:D,responseType:_,headers:ce,withCredentials:X="same-origin",fetchOptions:Oe}=ct(u),Te=t||fetch;_=_?(_+"").toLowerCase():"text";let G=Bn([x,E&&E.toAbortSignal()],O),$=null;const B=G&&G.unsubscribe&&(()=>{G.unsubscribe()});let Ae;try{if(D&&f&&d!=="get"&&d!=="head"&&(Ae=await g(ce,R))!==0){let L=new n(m,{method:"POST",body:R,duplex:"half"}),I;if(a.isFormData(R)&&(I=L.headers.get("content-type"))&&ce.setContentType(I),L.body){const[le,Q]=Be(Ae,ne(ke(D)));R=qe(L.body,Ie,le,Q)}}a.isString(X)||(X=X?"include":"omit");const T=o&&"credentials"in n.prototype,Ce={...Oe,signal:G,method:d.toUpperCase(),headers:ce.normalize().toJSON(),body:R,duplex:"half",credentials:T?X:void 0};$=o&&new n(m,Ce);let U=await(o?Te($,Oe):Te(m,Ce));const xe=l&&(_==="stream"||_==="response");if(l&&(P||xe&&B)){const L={};["status","statusText","headers"].forEach(_e=>{L[_e]=U[_e]});const I=a.toFiniteNumber(U.headers.get("content-length")),[le,Q]=P&&Be(I,ne(ke(P),!0))||[];U=new r(qe(U.body,Ie,le,()=>{Q&&Q(),B&&B()}),L)}_=_||"text";let ht=await h[a.findKey(h,_)||"text"](U,u);return!xe&&B&&B(),await new Promise((L,I)=>{it(L,I,{data:ht,headers:C.from(U.headers),status:U.status,statusText:U.statusText,config:u,request:$})})}catch(T){throw B&&B(),T&&T.name==="TypeError"&&/Load failed|fetch/i.test(T.message)?Object.assign(new y("Network Error",y.ERR_NETWORK,u,$,T&&T.response),{cause:T.cause||T}):y.from(T,T&&T.code,u,$,T&&T.response)}}},Mn=new Map,lt=e=>{let t=e&&e.env||{};const{fetch:n,Request:r,Response:s}=t,o=[r,s,n];let i=o.length,c=i,p,f,l=Mn;for(;c--;)p=o[c],f=l.get(p),f===void 0&&l.set(p,f=c?new Map:Hn(t)),l=f;return f};lt();const Se={http:rn,xhr:Dn,fetch:{get:lt}};a.forEach(Se,(e,t)=>{if(e){try{Object.defineProperty(e,"name",{value:t})}catch{}Object.defineProperty(e,"adapterName",{value:t})}});const ze=e=>`- ${e}`,$n=e=>a.isFunction(e)||e===null||e===!1;function zn(e,t){e=a.isArray(e)?e:[e];const{length:n}=e;let r,s;const o={};for(let i=0;i`adapter ${p} `+(f===!1?"is not supported by the environment":"is not available in the build"));let c=n?i.length>1?`since : +`+i.map(ze).join(` +`):" "+ze(i[0]):"as no adapter specified";throw new y("There is no suitable adapter to dispatch the request "+c,"ERR_NOT_SUPPORT")}return s}const ut={getAdapter:zn,adapters:Se};function pe(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new v(null,e)}function Je(e){return pe(e),e.headers=C.from(e.headers),e.data=de.call(e,e.transformRequest),["post","put","patch"].indexOf(e.method)!==-1&&e.headers.setContentType("application/x-www-form-urlencoded",!1),ut.getAdapter(e.adapter||K.adapter,e)(e).then(function(r){return pe(e),r.data=de.call(e,e.transformResponse,r),r.headers=C.from(r.headers),r},function(r){return ot(r)||(pe(e),r&&r.response&&(r.response.data=de.call(e,e.transformResponse,r.response),r.response.headers=C.from(r.response.headers))),Promise.reject(r)})}const ft="1.13.6",ae={};["object","boolean","number","function","string","symbol"].forEach((e,t)=>{ae[e]=function(r){return typeof r===e||"a"+(t<1?"n ":" ")+e}});const Ve={};ae.transitional=function(t,n,r){function s(o,i){return"[Axios v"+ft+"] Transitional option '"+o+"'"+i+(r?". "+r:"")}return(o,i,c)=>{if(t===!1)throw new y(s(i," has been removed"+(n?" in "+n:"")),y.ERR_DEPRECATED);return n&&!Ve[i]&&(Ve[i]=!0,console.warn(s(i," has been deprecated since v"+n+" and will be removed in the near future"))),t?t(o,i,c):!0}};ae.spelling=function(t){return(n,r)=>(console.warn(`${r} is likely a misspelling of ${t}`),!0)};function Jn(e,t,n){if(typeof e!="object")throw new y("options must be an object",y.ERR_BAD_OPTION_VALUE);const r=Object.keys(e);let s=r.length;for(;s-- >0;){const o=r[s],i=t[o];if(i){const c=e[o],p=c===void 0||i(c,o,e);if(p!==!0)throw new y("option "+o+" must be "+p,y.ERR_BAD_OPTION_VALUE);continue}if(n!==!0)throw new y("Unknown option "+o,y.ERR_BAD_OPTION)}}const te={assertOptions:Jn,validators:ae},N=te.validators;let j=class{constructor(t){this.defaults=t||{},this.interceptors={request:new Le,response:new Le}}async request(t,n){try{return await this._request(t,n)}catch(r){if(r instanceof Error){let s={};Error.captureStackTrace?Error.captureStackTrace(s):s=new Error;const o=s.stack?s.stack.replace(/^.+\n/,""):"";try{r.stack?o&&!String(r.stack).endsWith(o.replace(/^.+\n.+\n/,""))&&(r.stack+=` +`+o):r.stack=o}catch{}}throw r}}_request(t,n){typeof t=="string"?(n=n||{},n.url=t):n=t||{},n=q(this.defaults,n);const{transitional:r,paramsSerializer:s,headers:o}=n;r!==void 0&&te.assertOptions(r,{silentJSONParsing:N.transitional(N.boolean),forcedJSONParsing:N.transitional(N.boolean),clarifyTimeoutError:N.transitional(N.boolean),legacyInterceptorReqResOrdering:N.transitional(N.boolean)},!1),s!=null&&(a.isFunction(s)?n.paramsSerializer={serialize:s}:te.assertOptions(s,{encode:N.function,serialize:N.function},!0)),n.allowAbsoluteUrls!==void 0||(this.defaults.allowAbsoluteUrls!==void 0?n.allowAbsoluteUrls=this.defaults.allowAbsoluteUrls:n.allowAbsoluteUrls=!0),te.assertOptions(n,{baseUrl:N.spelling("baseURL"),withXsrfToken:N.spelling("withXSRFToken")},!0),n.method=(n.method||this.defaults.method||"get").toLowerCase();let i=o&&a.merge(o.common,o[n.method]);o&&a.forEach(["delete","get","head","post","put","patch","common"],u=>{delete o[u]}),n.headers=C.concat(i,o);const c=[];let p=!0;this.interceptors.request.forEach(function(m){if(typeof m.runWhen=="function"&&m.runWhen(n)===!1)return;p=p&&m.synchronous;const d=n.transitional||Ee;d&&d.legacyInterceptorReqResOrdering?c.unshift(m.fulfilled,m.rejected):c.push(m.fulfilled,m.rejected)});const f=[];this.interceptors.response.forEach(function(m){f.push(m.fulfilled,m.rejected)});let l,h=0,b;if(!p){const u=[Je.bind(this),void 0];for(u.unshift(...c),u.push(...f),b=u.length,l=Promise.resolve(n);h{if(!r._listeners)return;let o=r._listeners.length;for(;o-- >0;)r._listeners[o](s);r._listeners=null}),this.promise.then=s=>{let o;const i=new Promise(c=>{r.subscribe(c),o=c}).then(s);return i.cancel=function(){r.unsubscribe(o)},i},t(function(o,i,c){r.reason||(r.reason=new v(o,i,c),n(r.reason))})}throwIfRequested(){if(this.reason)throw this.reason}subscribe(t){if(this.reason){t(this.reason);return}this._listeners?this._listeners.push(t):this._listeners=[t]}unsubscribe(t){if(!this._listeners)return;const n=this._listeners.indexOf(t);n!==-1&&this._listeners.splice(n,1)}toAbortSignal(){const t=new AbortController,n=r=>{t.abort(r)};return this.subscribe(n),t.signal.unsubscribe=()=>this.unsubscribe(n),t.signal}static source(){let t;return{token:new dt(function(s){t=s}),cancel:t}}};function Wn(e){return function(n){return e.apply(null,n)}}function Kn(e){return a.isObject(e)&&e.isAxiosError===!0}const be={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511,WebServerIsDown:521,ConnectionTimedOut:522,OriginIsUnreachable:523,TimeoutOccurred:524,SslHandshakeFailed:525,InvalidSslCertificate:526};Object.entries(be).forEach(([e,t])=>{be[t]=e});function pt(e){const t=new j(e),n=We(j.prototype.request,t);return a.extend(n,j.prototype,t,{allOwnKeys:!0}),a.extend(n,t,null,{allOwnKeys:!0}),n.create=function(s){return pt(q(e,s))},n}const w=pt(K);w.Axios=j;w.CanceledError=v;w.CancelToken=Vn;w.isCancel=ot;w.VERSION=ft;w.toFormData=ie;w.AxiosError=y;w.Cancel=w.CanceledError;w.all=function(t){return Promise.all(t)};w.spread=Wn;w.isAxiosError=Kn;w.mergeConfig=q;w.AxiosHeaders=C;w.formToJSON=e=>st(a.isHTMLForm(e)?new FormData(e):e);w.getAdapter=ut.getAdapter;w.HttpStatusCode=be;w.default=w;const{Axios:Qn,AxiosError:Zn,CanceledError:Yn,isCancel:er,CancelToken:tr,VERSION:nr,all:rr,Cancel:sr,isAxiosError:or,spread:ir,toFormData:ar,AxiosHeaders:cr,HttpStatusCode:lr,formToJSON:ur,getAdapter:fr,mergeConfig:dr}=w;window.axios=w;window.axios.defaults.headers.common["X-Requested-With"]="XMLHttpRequest"; diff --git a/public/build/assets/app-DamXIAY7.css b/public/build/assets/app-DamXIAY7.css new file mode 100644 index 0000000..8e457e5 --- /dev/null +++ b/public/build/assets/app-DamXIAY7.css @@ -0,0 +1 @@ +*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Figtree,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.visible{visibility:visible}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.right-0{right:0}.z-0{z-index:0}.z-10{z-index:10}.float-right{float:right}.float-left{float:left}.m-auto{margin:auto}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.-ml-px{margin-left:-1px}.-mt-2{margin-top:-.5rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.me-1{margin-inline-end:.25rem}.me-2{margin-inline-end:.5rem}.me-3{margin-inline-end:.75rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-6{margin-top:1.5rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[32\.5rem\]{height:32.5rem}.h-\[35\.5rem\]{height:35.5rem}.max-h-32{max-height:8rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-\[8rem\]{width:8rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-full{max-width:100%}.flex-1{flex:1 1 0%}.flex-none{flex:none}.shrink-0{flex-shrink:0}.flex-grow{flex-grow:1}.border-collapse{border-collapse:collapse}.origin-top-right{transform-origin:top right}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.items-baseline{align-items:baseline}.justify-between{justify-content:space-between}.justify-items-center{justify-items:center}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-6{gap:1.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.overflow-x-auto{overflow-x:auto}.overflow-y-hidden{overflow-y:hidden}.overflow-x-scroll{overflow-x:scroll}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.border{border-width:1px}.border-0{border-width:0px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-gray-300{--tw-border-opacity: 1;border-color:rgb(209 213 219 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.border-l-red-500{--tw-border-opacity: 1;border-left-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.bg-gray-200{--tw-bg-opacity: 1;background-color:rgb(229 231 235 / var(--tw-bg-opacity, 1))}.bg-gray-200\/80{background-color:#e5e7ebcc}.bg-red-500\/20{background-color:#ef444433}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.fill-red-500{fill:#ef4444}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.pb-12{padding-bottom:3rem}.pb-3{padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-4{padding-left:1rem}.pr-4{padding-right:1rem}.ps-4{padding-inline-start:1rem}.pt-0{padding-top:0}.pt-4{padding-top:1rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-end{text-align:end}.align-middle{vertical-align:middle}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.font-sans{font-family:Figtree,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.text-2xl{font-size:1.5rem;line-height:2rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.capitalize{text-transform:capitalize}.leading-5{line-height:1.25rem}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-50{--tw-text-opacity: 1;color:rgb(249 250 251 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.opacity-25{opacity:.25}.opacity-50{opacity:.5}.opacity-75{opacity:.75}.shadow-none{--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-gray-300{--tw-ring-opacity: 1;--tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity, 1))}.ring-gray-900\/5{--tw-ring-color: rgb(17 24 39 / .05)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-150{transition-duration:.15s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.default\:col-span-full:default{grid-column:1 / -1}.default\:row-span-1:default{grid-row:span 1 / span 1}.hover\:rounded-b-md:hover{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.hover\:rounded-t-md:hover{border-top-left-radius:.375rem;border-top-right-radius:.375rem}.hover\:bg-gray-100:hover{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-100\/75:hover{background-color:#f3f4f6bf}.hover\:text-gray-400:hover{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.hover\:underline:hover{text-decoration-line:underline}.focus\:z-10:focus{z-index:10}.focus\:border-blue-300:focus{--tw-border-opacity: 1;border-color:rgb(147 197 253 / var(--tw-border-opacity, 1))}.focus\:text-gray-500:focus{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.active\:bg-gray-100:active{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity, 1))}.active\:text-gray-500:active{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.active\:text-gray-700:active{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity, 1))}@media (min-width: 640px){.sm\:col-span-1{grid-column:span 1 / span 1}.sm\:col-span-2{grid-column:span 2 / span 2}.sm\:mt-10{margin-top:2.5rem}.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:flex-1{flex:1 1 0%}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}.sm\:gap-6{gap:1.5rem}.sm\:p-12{padding:3rem}.sm\:py-5{padding-top:1.25rem;padding-bottom:1.25rem}.sm\:text-3xl{font-size:1.875rem;line-height:2.25rem}}@media (min-width: 768px){.md\:block{display:block}.md\:inline{display:inline}.md\:flex{display:flex}.md\:hidden{display:none}.md\:min-w-64{min-width:16rem}.md\:max-w-80{max-width:20rem}.md\:items-center{align-items:center}.md\:justify-between{justify-content:space-between}.md\:gap-2{gap:.5rem}}@media (min-width: 1024px){.lg\:block{display:block}.lg\:inline-block{display:inline-block}.lg\:w-\[12rem\]{width:12rem}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:text-2xl{font-size:1.5rem;line-height:2rem}.lg\:text-base{font-size:1rem;line-height:1.5rem}.lg\:text-sm{font-size:.875rem;line-height:1.25rem}.default\:lg\:col-span-6:default{grid-column:span 6 / span 6}}.rtl\:flex-row-reverse:where([dir=rtl],[dir=rtl] *){flex-direction:row-reverse}@media (prefers-color-scheme: dark){.dark\:block{display:block}.dark\:hidden{display:none}.dark\:border{border-width:1px}.dark\:border-gray-600{--tw-border-opacity: 1;border-color:rgb(75 85 99 / var(--tw-border-opacity, 1))}.dark\:border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.dark\:border-gray-800{--tw-border-opacity: 1;border-color:rgb(31 41 55 / var(--tw-border-opacity, 1))}.dark\:border-gray-900{--tw-border-opacity: 1;border-color:rgb(17 24 39 / var(--tw-border-opacity, 1))}.dark\:border-l-red-500{--tw-border-opacity: 1;border-left-color:rgb(239 68 68 / var(--tw-border-opacity, 1))}.dark\:bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.dark\:bg-gray-900\/80{background-color:#111827cc}.dark\:bg-gray-950\/95{background-color:#030712f2}.dark\:bg-red-500\/20{background-color:#ef444433}.dark\:text-gray-100{--tw-text-opacity: 1;color:rgb(243 244 246 / var(--tw-text-opacity, 1))}.dark\:text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.dark\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.dark\:text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.dark\:text-gray-950{--tw-text-opacity: 1;color:rgb(3 7 18 / var(--tw-text-opacity, 1))}.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.dark\:ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.dark\:ring-gray-800{--tw-ring-opacity: 1;--tw-ring-color: rgb(31 41 55 / var(--tw-ring-opacity, 1))}.dark\:hover\:bg-gray-700:hover{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.dark\:hover\:bg-gray-800\/75:hover{background-color:#1f2937bf}.dark\:hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.dark\:hover\:text-gray-500:hover{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.dark\:focus\:border-blue-700:focus{--tw-border-opacity: 1;border-color:rgb(29 78 216 / var(--tw-border-opacity, 1))}.dark\:focus\:border-blue-800:focus{--tw-border-opacity: 1;border-color:rgb(30 64 175 / var(--tw-border-opacity, 1))}.dark\:focus\:text-gray-500:focus{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.dark\:active\:bg-gray-700:active{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.dark\:active\:text-gray-300:active{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}} diff --git a/public/build/manifest.json b/public/build/manifest.json new file mode 100644 index 0000000..e0d82ed --- /dev/null +++ b/public/build/manifest.json @@ -0,0 +1,13 @@ +{ + "resources/css/app.css": { + "file": "assets/app-DamXIAY7.css", + "src": "resources/css/app.css", + "isEntry": true + }, + "resources/js/app.js": { + "file": "assets/app-BuG9aa18.js", + "name": "app", + "src": "resources/js/app.js", + "isEntry": true + } +} \ No newline at end of file diff --git a/public/uploads/transfer_proofs/1777959156_images.jpg b/public/uploads/transfer_proofs/1777959156_images.jpg new file mode 100644 index 0000000..13370e2 Binary files /dev/null and b/public/uploads/transfer_proofs/1777959156_images.jpg differ diff --git a/public/uploads/transfer_proofs/1777959673_images.jpg b/public/uploads/transfer_proofs/1777959673_images.jpg new file mode 100644 index 0000000..13370e2 Binary files /dev/null and b/public/uploads/transfer_proofs/1777959673_images.jpg differ diff --git a/public/uploads/transfer_proofs/1777959841_images.jpg b/public/uploads/transfer_proofs/1777959841_images.jpg new file mode 100644 index 0000000..13370e2 Binary files /dev/null and b/public/uploads/transfer_proofs/1777959841_images.jpg differ diff --git a/public/uploads/transfer_proofs/1777959915_images.jpg b/public/uploads/transfer_proofs/1777959915_images.jpg new file mode 100644 index 0000000..13370e2 Binary files /dev/null and b/public/uploads/transfer_proofs/1777959915_images.jpg differ diff --git a/public/uploads/transfer_proofs/1777960005_HP-580-Print-Scan-Copy-3.png b/public/uploads/transfer_proofs/1777960005_HP-580-Print-Scan-Copy-3.png new file mode 100644 index 0000000..d1a29aa Binary files /dev/null and b/public/uploads/transfer_proofs/1777960005_HP-580-Print-Scan-Copy-3.png differ diff --git a/public/uploads/transfer_proofs/1777960043_images.jpg b/public/uploads/transfer_proofs/1777960043_images.jpg new file mode 100644 index 0000000..13370e2 Binary files /dev/null and b/public/uploads/transfer_proofs/1777960043_images.jpg differ diff --git a/public/uploads/transfer_proofs/1777960130_essential_mouse_20190219133302924.jpg b/public/uploads/transfer_proofs/1777960130_essential_mouse_20190219133302924.jpg new file mode 100644 index 0000000..6a2b2fe Binary files /dev/null and b/public/uploads/transfer_proofs/1777960130_essential_mouse_20190219133302924.jpg differ diff --git a/public/uploads/transfer_proofs/1777960220_essential_mouse_20190219133302924.jpg b/public/uploads/transfer_proofs/1777960220_essential_mouse_20190219133302924.jpg new file mode 100644 index 0000000..6a2b2fe Binary files /dev/null and b/public/uploads/transfer_proofs/1777960220_essential_mouse_20190219133302924.jpg differ diff --git a/public/uploads/transfer_proofs/1777960356_essential_mouse_20190219133302924.jpg b/public/uploads/transfer_proofs/1777960356_essential_mouse_20190219133302924.jpg new file mode 100644 index 0000000..6a2b2fe Binary files /dev/null and b/public/uploads/transfer_proofs/1777960356_essential_mouse_20190219133302924.jpg differ diff --git a/public/uploads/transfer_proofs/1777960391_essential_mouse_20190219133302924.jpg b/public/uploads/transfer_proofs/1777960391_essential_mouse_20190219133302924.jpg new file mode 100644 index 0000000..6a2b2fe Binary files /dev/null and b/public/uploads/transfer_proofs/1777960391_essential_mouse_20190219133302924.jpg differ diff --git a/public/uploads/transfer_proofs/1777960594_Hot-Stone-title-Pic-scaled.jpg b/public/uploads/transfer_proofs/1777960594_Hot-Stone-title-Pic-scaled.jpg new file mode 100644 index 0000000..bb7c644 Binary files /dev/null and b/public/uploads/transfer_proofs/1777960594_Hot-Stone-title-Pic-scaled.jpg differ diff --git a/public/uploads/transfer_proofs/1777960764_Hot-Stone-title-Pic-scaled.jpg b/public/uploads/transfer_proofs/1777960764_Hot-Stone-title-Pic-scaled.jpg new file mode 100644 index 0000000..bb7c644 Binary files /dev/null and b/public/uploads/transfer_proofs/1777960764_Hot-Stone-title-Pic-scaled.jpg differ diff --git a/public/uploads/transfer_proofs/1777961229_HP-580-Print-Scan-Copy-3.png b/public/uploads/transfer_proofs/1777961229_HP-580-Print-Scan-Copy-3.png new file mode 100644 index 0000000..d1a29aa Binary files /dev/null and b/public/uploads/transfer_proofs/1777961229_HP-580-Print-Scan-Copy-3.png differ diff --git a/public/uploads/transfer_proofs/1777962229_HP-580-Print-Scan-Copy-3.png b/public/uploads/transfer_proofs/1777962229_HP-580-Print-Scan-Copy-3.png new file mode 100644 index 0000000..d1a29aa Binary files /dev/null and b/public/uploads/transfer_proofs/1777962229_HP-580-Print-Scan-Copy-3.png differ diff --git a/public/uploads/transfer_proofs/1777962502_logo_sms3.png b/public/uploads/transfer_proofs/1777962502_logo_sms3.png new file mode 100644 index 0000000..bcf2ae6 Binary files /dev/null and b/public/uploads/transfer_proofs/1777962502_logo_sms3.png differ diff --git a/public/uploads/transfer_proofs/1777962600_Header_TIA2.jpg b/public/uploads/transfer_proofs/1777962600_Header_TIA2.jpg new file mode 100644 index 0000000..481dec1 Binary files /dev/null and b/public/uploads/transfer_proofs/1777962600_Header_TIA2.jpg differ diff --git a/public/uploads/transfer_proofs/1777970099_pd024701_e330_overview.png b/public/uploads/transfer_proofs/1777970099_pd024701_e330_overview.png new file mode 100644 index 0000000..f6f8192 Binary files /dev/null and b/public/uploads/transfer_proofs/1777970099_pd024701_e330_overview.png differ diff --git a/public/uploads/transfer_proofs/1778474297_(INSP-FPP-2018-0173)_Inspection_Report_TIA_Signed.pdf b/public/uploads/transfer_proofs/1778474297_(INSP-FPP-2018-0173)_Inspection_Report_TIA_Signed.pdf new file mode 100644 index 0000000..ceacaf0 Binary files /dev/null and b/public/uploads/transfer_proofs/1778474297_(INSP-FPP-2018-0173)_Inspection_Report_TIA_Signed.pdf differ diff --git a/public/uploads/transfer_proofs/1778478023_LAPORAN_TIKET_20APR2026_153251.pdf b/public/uploads/transfer_proofs/1778478023_LAPORAN_TIKET_20APR2026_153251.pdf new file mode 100644 index 0000000..69e7c6f Binary files /dev/null and b/public/uploads/transfer_proofs/1778478023_LAPORAN_TIKET_20APR2026_153251.pdf differ diff --git a/public/uploads/transfer_proofs/1778750341_VMAR-F1006-11_Report_AMV_Triclofem_Impurity_F-searchable.pdf b/public/uploads/transfer_proofs/1778750341_VMAR-F1006-11_Report_AMV_Triclofem_Impurity_F-searchable.pdf new file mode 100644 index 0000000..8118c2b Binary files /dev/null and b/public/uploads/transfer_proofs/1778750341_VMAR-F1006-11_Report_AMV_Triclofem_Impurity_F-searchable.pdf differ diff --git a/resources/views/backend/pages/audit_trail/index.blade.php b/resources/views/backend/pages/audit_trail/index.blade.php index eb2a654..5bb778f 100644 --- a/resources/views/backend/pages/audit_trail/index.blade.php +++ b/resources/views/backend/pages/audit_trail/index.blade.php @@ -1,23 +1,31 @@ @extends('layouts.app') -@section('title') - {{ $pageInfo['title'] }} -@endsection - +@section('title', $pageInfo['title']) @section('admin-content')
-
+
-

{{ $pageInfo['title'] }}

+

{{ $pageInfo['title'] }}

- + +
+ + Reset Cache + + +
+ @csrf + @method('DELETE') + +
+
@@ -25,82 +33,137 @@
-
-
-
-
-

{{ $pageInfo['title'] }}

-
-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - @foreach ($data as $item) - - - - - - - - - - - @endforeach - -
{{ __('User') }}{{ __('Ip Address') }}{{ __('Action') }}{{ __('Activity') }}BrowserUser AgentTanggalWaktu
- {{ $item->user ? $item->user->username : preg_replace('/\).*$/', '', explode('(', $item->activity)[1]) }} - {{ $item->ip_address }} - @php - $color = 'info'; - if ($item->action == 'Create') { - $color = 'success'; - } elseif ($item->action == 'Update') { - $color = 'warning'; - } elseif ($item->action == 'Delete' || $item->action == 'Error') { - $color = 'danger'; - } - @endphp -
- {{ $item->action }} -
-
{{ $item->activity }}{{ $item->browser }}{{ $item->user_agent }}{{ $item->created_at }}{{ $item->created_at->diffForHumans() }}
+ + +
+
+
Panel Pencarian & Filter
+
+
+
+
+ +
+ + +
+ + +
+ + +
+ + +
+ +
+ +
+ s/d +
+
+ + +
+ + + + +
+ +
+ + +
+
+ + Export Hasil Pencarian ke Excel + + + * Menampilkan {{ $data->firstItem() }} sampai {{ $data->lastItem() }} dari {{ $data->total() }} log. + +
+
+
+
+
+ + +
+
+ @include('backend.layouts.partials.messages') +
+ + + + + + + + + + + + + @foreach ($data as $item) + + + + + + + + + @endforeach + +
UserIP AddressAksiAktivitasBrowserWaktu
+ {{ $item->user->username ?? 'System' }} + {{ $item->ip_address }} + @php + $badge = match($item->action) { + 'Insert', 'Create' => 'success', + 'Update' => 'warning', + 'Delete', 'Reject', 'Error' => 'danger', + 'Approve' => 'primary', + default => 'info' + }; + @endphp + {{ $item->action }} + {{ $item->activity }}{{ $item->browser }} +
+ {{ $item->created_at->format('d M Y') }} + {{ $item->created_at->format('H:i') }} ({{ $item->created_at->diffForHumans() }}) +
+
+
+
+ +
-@endsection - -@section('scripts') - -@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/budget_control/index.blade.php b/resources/views/backend/pages/budget_control/index.blade.php index 96ceda3..4cea572 100644 --- a/resources/views/backend/pages/budget_control/index.blade.php +++ b/resources/views/backend/pages/budget_control/index.blade.php @@ -1,138 +1,273 @@ @extends('layouts.app') -@section('title') - Dashboard -@endsection +@section('title', 'Active Budgets Control') @section('admin-content')
-

Active Budgets

+

Active Budgets Control

+
-
+ + {{-- --- BAGIAN 1: PANEL FILTER DINAMIS --- --}} +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+
+ +
-

Current Active Budget

-

- @if (auth()->user()->can('budget_control.create')) - - Add New Budget - - @endif -

-
+
+

Current Active Budget

+ @if (auth()->user()->can('budget_control.create')) +
+ @if(auth()->user()->hasAnyRole(['Accounting', 'accounting', 'Admin Region', 'Superadmin', 'Super Admin'])) + + @endif + + + Add New Budget + +
+ @endif +
+
@include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - @php - use App\Helpers\BudgetHelper; - @endphp - @foreach ($data as $item) - - - - - - - - - - - - - @endforeach - -
#PenerimaCabangPengirimTotalSisa Budget TersediaKeteranganPeriodeStatusAksi
{{ $loop->iteration }}{{ $item->receiver->name }}{{ $item->cabang->name }}{{ $item->sender->name }}{{ number_format($item->amount, 0, ',', '.') }}{{ number_format($item->availableBudget, 0, ',', '.') }}{{ $item->remarks }}{{ strtoupper($item->periode_month) }} {{ $item->periode_year }} - @if ($item->status == 'Unassigned') - {{ $item->status }} - @elseif ($item->status == 'Assigned') - {{ $item->status }} - @else - {{ $item->status }} - @endif - - @if (auth()->user()->can('budget_control.delete')) -
- @csrf - @method('DELETE') - -
- @endif -
-
-
+
+ + + + + + + + + + + + + + + + + @foreach ($data as $item) + @php + $realtimeBudget = $item->availableBudget ?? 0; + @endphp + + + + + + + + + + + + + @endforeach + +
#Penerima / CabangPengirimAssigned BudgetReal-time Sisa BudgetPeriodeStatus BudgetStatus TransferBuktiAksi
{{ ($data->currentPage() - 1) * $data->perPage() + $loop->iteration }} +
{{ $item->receiver->name ?? '-' }}
+ {{ $item->cabang->name ?? '-' }} +
{{ $item->sender->name ?? '-' }} +
Rp {{ number_format($item->amount, 0, ',', '.') }}
+
+
+ Rp {{ number_format($realtimeBudget, 0, ',', '.') }} +
+
+ + {{ strtoupper($item->periode_month) }} {{ $item->periode_year }} + + + + {{ $item->status }} + +
+ {{ $item->created_at->format('d M Y H:i') }} WIB +
+
+ @php + $tStatus = $item->transfer_status ?? 'Pending'; + $badgeClass = match(strtolower($tStatus)) { + 'validated' => 'badge-success', + 'waiting approval' => 'badge-info', + 'rejected' => 'badge-danger', + default => 'badge-secondary' + }; + @endphp + {{ $tStatus }} + + @if($item->transfer_proof) + + View + + @else + - + @endif + +
+ @if(strtolower($item->transfer_status) != 'validated') + + @endif + + @if(Auth::user()->hasAnyRole(['accounting', 'Accounting', 'admin', 'Admin', 'Superadmin']) && strtolower($item->transfer_status) == 'waiting approval') +
+ @csrf + +
+
+ @csrf + +
+ @endif + + @if (auth()->user()->can('budget_control.delete')) +
+ @csrf @method('DELETE') + +
+ @endif +
+
+
+ + {{-- --- BAGIAN 2: NAVIGASI HALAMAN (SERVER-SIDE) --- --}} +
+
+ Menampilkan {{ $data->firstItem() }} sampai {{ $data->lastItem() }} dari {{ $data->total() }} data +
+
+ {{ $data->appends(request()->query())->links('pagination::bootstrap-4') }} +
+
+
+ + @include('backend.pages.budget_control.partials.modals') @endsection @section('scripts') -@endsection + function toggleUploadType() { + const isRegion = document.getElementById('type_region').checked; + document.getElementById('group_region').classList.toggle('d-none', !isRegion); + document.getElementById('group_cabang').classList.toggle('d-none', isRegion); + } + + $(document).ready(function () { + // Inisialisasi Select2 untuk Filter + $('.select2-filter').select2({ + theme: 'bootstrap4', + width: '100%' + }).on('change', function() { + $('#filter-form').submit(); + }); + + // Inisialisasi DataTables + if ($('#dataTable').length) { + $('#dataTable').DataTable({ + paging: false, + info: false, + responsive: false, + dom: 'Blfrtip', + buttons: [ + { extend: 'excel', className: 'btn-sm btn-success', text: ' Excel' }, + { extend: 'pdf', className: 'btn-sm btn-danger', text: ' PDF' } + ] + }); + } + + $(document).on('click', '.btn-upload', function() { + $('#budget_id').val($(this).data('id')); + }); + + // Select2 di dalam modal + $('#modalBulkUpload').on('shown.bs.modal', function () { + $('.select2_modal').select2({ + theme: 'bootstrap4', + width: '100%', + dropdownParent: $('#modalBulkUpload') + }); + }); + }); + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/budget_control/log.blade.php b/resources/views/backend/pages/budget_control/log.blade.php index de0440b..0466465 100644 --- a/resources/views/backend/pages/budget_control/log.blade.php +++ b/resources/views/backend/pages/budget_control/log.blade.php @@ -1,90 +1,270 @@ @extends('layouts.app') -@section('title') - Dashboard -@endsection +@section('title', 'Riwayat Transfer & Realisasi') @section('admin-content') -
-
-
-
-

Log Transfer Budgets

-
-
- +
+
+
+
+

Log Riwayat Budget & Transaksi

+
+
+ +
+
+
+
+ +
+
+ + {{-- --- BAGIAN BARU: PANEL FILTER --- --}} +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + +
+
+
+
+
+
+ + {{-- --- BAGIAN GRAFIK PERBANDINGAN (TETAP ADA) --- --}} +
+
+
+
+

+ Perbandingan Petty Cash vs Realisasi (Halaman Ini) +

+
+
+
+ +
+
-
-
-
-
-
-

Log Transfer Budgets

-
-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - @foreach ($data as $item) - - - - - - - - - - - @endforeach - -
#PenerimaCabangPengirimTotalKeteranganPeriodeStatus
{{ $loop->iteration }}{{ $item->receiver->name }}{{ $item->cabang->name }}{{ $item->sender->name }}{{ number_format($item->amount, 0, ',', '.') }}{{ $item->remarks }}{{ strtoupper($item->periode_month) }} {{ $item->periode_year }} - @if ($item->status == 'Unassigned') - {{ $item->status }} - @elseif ($item->status == 'Assigned') - {{ $item->status }} - @else - {{ $item->status }} - @endif -
-
-
+ + {{-- --- TABEL DATA --- --}} +
+
+
+
Detail Transaksi Riwayat
+
+ +
+ + + + + + + + + + + + + + + + @forelse ($data as $item) + + + + + + + + + + + + + + + @empty + + + + @endforelse + +
#Cabang / RegionPeriodeTotal Budget (Petty Cash)Total Debet (Masuk)Total Kredit (Keluar)Realisasi (Sisa)Waktu AssignStatus
{{ ($data->currentPage() - 1) * $data->perPage() + $loop->iteration }} +
{{ $item->cabang->name ?? '-' }}
+ {{ $item->cabang->region->name ?? '-' }} +
+ + {{ strtoupper($item->periode_month) }} {{ $item->periode_year }} + + + Rp {{ number_format($item->total_budget, 0, ',', '.') }} + + Rp {{ number_format($item->total_debet, 0, ',', '.') }} + + Rp {{ number_format($item->total_credit, 0, ',', '.') }} + + Rp {{ number_format($item->realisasi, 0, ',', '.') }} + + + {{ $item->created_at->format('d M Y') }} + + + {{ $item->created_at->format('H:i') }} WIB + + + + {{ $item->status }} + +
+
+ Belum ada riwayat budget yang tercatat. +
+
+ + {{-- --- PAGINATION LARAVEL (TETAP ADA) --- --}} +
+
+ Menampilkan {{ $data->firstItem() }} sampai {{ $data->lastItem() }} dari {{ $data->total() }} data +
+
+ {{ $data->appends(request()->query())->links('pagination::bootstrap-4') }} +
+
+
-
+
+
@endsection @section('scripts') - -@endsection + + + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/budget_control/partials/modals.blade.php b/resources/views/backend/pages/budget_control/partials/modals.blade.php new file mode 100644 index 0000000..00bb095 --- /dev/null +++ b/resources/views/backend/pages/budget_control/partials/modals.blade.php @@ -0,0 +1,113 @@ +{{-- Modal Upload Bukti Transfer --}} + + +{{-- Modal Bulk Upload Budget --}} + \ No newline at end of file diff --git a/resources/views/backend/pages/dashboard/index.blade.php b/resources/views/backend/pages/dashboard/index.blade.php index f0f47f3..8ae8308 100644 --- a/resources/views/backend/pages/dashboard/index.blade.php +++ b/resources/views/backend/pages/dashboard/index.blade.php @@ -1,125 +1,357 @@ @extends('layouts.app') -@section('title') - Dashboard +@section('title', 'Dashboard Operational Analytics') + +@section('styles') + + +{{-- Tambahan CDN Select2 untuk menjamin inisialisasi select2 dropdown berjalan lancar --}} + + + @endsection @section('admin-content') -
-
-
-
-

Home

+
+ + {{-- 1. HEADER & LIVE CLOCK --}} +
+
+ Waktu Sistem: + + Memuat... + +
+
+
+ + Periode Input: {{ $periodeLabel ?? '-' }} +
+
+
+ + {{-- 2. KARTU STATISTIK --}} +
+
+
+

{{ $totalRegions ?? 0 }}

Total Region

+
+
+
+
+
+

{{ $totalCabangs ?? 0 }}

Cabang Aktif

+
+
+
+
+
+

{{ $pendingCount ?? 0 }}

Total Dokumen Pending

+
+
+
+
+
+
+

{{ $totalKategori ?? 0 }}

+

Master Kategori

-
- +
+
+
+
+ + {{-- FILTER KHUSUS GRAFIK PERFORMA --}} +
+
+
+ + + + + +
+
Filter Performa Closing Expense
+
+ + + + + +
+
+
+
+
+ + {{-- 3. GRAFIK PERFORMA CLOSING EXPENSE --}} +
Respon Speed Expense
+
+
+
+
+

5 Cabang Tercepat

+
+
+
-
-
-
- -
- -
-
-
-

Rp {{ number_format($forms['vehicle'] + $forms['entertainment'] + $forms['meeting'] + $forms['upcountry'] + $forms['others'], 0, ',', '.') }}

-

All Approved Expenses

-
-
- -
- - More info - -
+
+
+
+

5 Cabang Terlama

- -
-
-
-

Rp {{ number_format($forms['upcountry'], 0, ',', '.') }}

-

Approved Up Country Expenses

-
-
- -
- - More info - -
-
- -
-
-
-

Rp {{ number_format($forms['vehicle'], 0, ',', '.') }}

-

Approved Vehicle Running Cost Expenses

-
-
- -
- - More info - -
-
- -
-
-
-

Rp {{ number_format($forms['entertainment'], 0, ',', '.') }}

-

Approved Entertainment Presentation Expenses

-
-
- -
- - More info - -
-
- -
-
-
-

Rp {{ number_format($forms['meeting'], 0, ',', '.') }}

-

Approved Meeting Seminar Expenses

-
-
- -
- - More info - -
-
- -
-
-
-

Rp {{ number_format($forms['others'], 0, ',', '.') }}

-

Approved Other Expenses

-
-
- -
- - More info - -
-
-
- -
-
-

Welcome to {{ config('app.name') }} (development version).

+
+
-
+
+ + {{-- 4. FILTER PANEL TABEL PENDING --}} +
+
+
+
Tabel Monitoring Dokumen Pending
+ {{-- Container Tombol Excel --}} +
+
+ +
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + +
+
+
+
+
+
+ + {{-- 5. TABEL MONITORING --}} +
+
+ + + + + + + + + + + + + + + + + + + @php $currentRegion = null; @endphp + @forelse($cabangDetails as $detail) + @if($currentRegion != ($detail->region_id ?? null)) + + + + @php $currentRegion = $detail->region_id ?? null; @endphp + @endif + + + + + + + + + + + + + @empty + + + + @endforelse + +
+ Region + + Cabang + Rincian Dokumen Pending + Total + Aksi
UCVCENMTOT
+ REGION: {{ $detail->region->name ?? 'TANPA REGION' }} +
{{ $detail->region->name ?? '-' }}{{ $detail->name }}{{ $detail->pending_details['up_country'] ?? 0 }}{{ $detail->pending_details['vehicle'] ?? 0 }}{{ $detail->pending_details['entertainment'] ?? 0 }}{{ $detail->pending_details['meeting'] ?? 0 }}{{ $detail->pending_details['others'] ?? 0 }}{{ $detail->total_pending }} + +
+ Data tidak ditemukan untuk filter ini. +
+
+ +
+ +@endsection + +@section('scripts') +{{-- Library Wajib DataTables --}} + + +{{-- Library Wajib untuk Tombol Export --}} + + + + +{{-- Library Grafik --}} + + +{{-- Library Select2 untuk keindahan select input --}} + + + @endsection \ No newline at end of file diff --git a/resources/views/backend/pages/dashboard/show.blade.php b/resources/views/backend/pages/dashboard/show.blade.php new file mode 100644 index 0000000..175a6bc --- /dev/null +++ b/resources/views/backend/pages/dashboard/show.blade.php @@ -0,0 +1,42 @@ +@extends('layouts.app') + +@section('admin-content') +
+
+
+

Detail Pending: {{ $cabang->name }} ({{ $cabang->region->name ?? '' }})

+
+ +
+
+
+ {{-- Loop menggunakan variabel $data yang dikirim dari controller --}} + @foreach($data as $label => $items) + @if($items->count() > 0) +
{{ $label }}
+ + + + + + + + + + + @foreach($items as $item) + + + + + + + @endforeach + +
No. ExpenseTanggalTotalStatus
{{ $item->expense_number }}{{ \Carbon\Carbon::parse($item->tanggal)->format('d/m/Y') }}Rp {{ number_format($item->total, 0, ',', '.') }}{{ $item->status }}
+ @endif + @endforeach +
+
+
+@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/entertainment/create.blade.php b/resources/views/backend/pages/forms/entertainment/create.blade.php index 41c0724..a4c848e 100644 --- a/resources/views/backend/pages/forms/entertainment/create.blade.php +++ b/resources/views/backend/pages/forms/entertainment/create.blade.php @@ -1,11 +1,12 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') +
@@ -16,7 +17,6 @@
@@ -45,7 +45,7 @@
-
+ @csrf @include('backend.layouts.partials.messages')
@@ -57,7 +57,7 @@
-
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
- +
@@ -122,13 +138,13 @@
- +
-

Submitting, please wait...

+

Submitting, please wait...

@@ -137,21 +153,50 @@
@include('backend.pages.forms.entertainment.partials.attachment-modal') @include('backend.pages.forms.entertainment.partials.attachment-scripts') -@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/entertainment/edit.blade.php b/resources/views/backend/pages/forms/entertainment/edit.blade.php index 0db168f..d6b70fb 100644 --- a/resources/views/backend/pages/forms/entertainment/edit.blade.php +++ b/resources/views/backend/pages/forms/entertainment/edit.blade.php @@ -1,11 +1,12 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') +
@@ -16,12 +17,12 @@
+ +
-
+ @csrf @method('PUT') @include('backend.layouts.partials.messages') @@ -53,14 +51,15 @@
- + {{-- Diubah menjadi input date murni --}} +
@@ -79,9 +78,25 @@
-
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
- +
@@ -89,6 +104,7 @@
+ {{-- Lampiran Existing --}}

Lampiran Saat Ini
@@ -100,55 +116,41 @@ Nama File Preview Download - Delete + Aksi @forelse ($attachments as $attachment) - {{ $attachment['category_label'] ?? '-' }} + {{ ucwords(str_replace('_', ' ', $attachment['file_category'])) }} {{ $attachment['filename'] }} - + + + Download + + + - - @if (!empty($attachment['download_url'])) - Download - @else - Tidak tersedia - @endif - - - @if (!empty($attachment['can_delete'])) - - @else - Tidak tersedia - @endif - @empty - - Belum ada attachment. - + Belum ada lampiran tersimpan. @endforelse
-
-
+ {{-- Lampiran Baru --}} +
Tambah Lampiran Baru
- +
@@ -166,27 +168,21 @@ - - - +
Kategori
Belum ada attachment.
Belum ada lampiran baru.
- - Maksimum 10 MB per file. Tidak diperbolehkan: {{ implode(', ', $entertainmentBlockedExtensions ?? []) }}. -
- +
-
-
+

Submitting, please wait...

@@ -196,21 +192,37 @@
@include('backend.pages.forms.entertainment.partials.attachment-modal') @include('backend.pages.forms.entertainment.partials.attachment-scripts') -@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/entertainment/index.blade.php b/resources/views/backend/pages/forms/entertainment/index.blade.php index 1c720bd..a04b7f3 100644 --- a/resources/views/backend/pages/forms/entertainment/index.blade.php +++ b/resources/views/backend/pages/forms/entertainment/index.blade.php @@ -1,21 +1,77 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') - - @php - $outstandingIdCollection = collect($outstandingIds ?? []); - @endphp + + + @php + $outstandingIdCollection = collect($outstandingIds ?? []); + + // ========================================== + // FILTER PERIODE BERJALAN KHUSUS UNTUK MINI CARDS + // (Sinkronisasi Angka Realtime Siklus Tanggal 11-13) + // ========================================== + $startDayIndex = (int) env('STARTING_DATE', 11); + $closingDayIndex = (int) env('CLOSING_DATE', 13); + $nowIndex = \Carbon\Carbon::now(); + + if ($nowIndex->day >= $startDayIndex) { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->addMonth()->year, $nowIndex->copy()->addMonth()->month, $closingDayIndex)->endOfDay(); + } else { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->subMonth()->year, $nowIndex->copy()->subMonth()->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $closingDayIndex)->endOfDay(); + } + + // Filter koleksi data pengajuan murni yang berada dalam rentang aktif berjalan + $currentPeriodForms = $forms->filter(function($form) use ($startPeriodIndex, $endPeriodIndex) { + $tanggalForm = \Carbon\Carbon::parse($form->tanggal); + return $tanggalForm->between($startPeriodIndex, $endPeriodIndex); + }); + @endphp +
@@ -25,659 +81,405 @@
-
-
-
- -
-
-
-

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

-

Total Expense

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'), 0, ',', '.') }}

-

Approved Expenses

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

-

Pending Expenses

-
-
- -
-
-
- -
-
-
-

{{ $outstandingCount }}

-

Outstanding Expenses

-

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

-

Klik untuk menampilkan detail

-
-
- -
-
-
- @if($availableBudget > 0) -
-
-
-

Rp {{ number_format($availableBudget, 0, ',', '.') }}

-

Available Budget

-
-
- -
-
-
- @endif -
-
-
+ +
+
+
+
+
+
+

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

+

Total Expense

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed', 'Final Approved'])->sum('approved_total'), 0, ',', '.') }}

+

Approved Expenses

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

+

Pending Expenses

+
+
+
+
+
+
+
+

{{ $outstandingCount }}

+

Outstanding Expenses

+

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

+

Klik untuk menampilkan detail

+
+
+
+
+ @if($availableBudget > 0) +
+
+
+

Rp {{ number_format($availableBudget, 0, ',', '.') }}

+

Available Budget

+
+
+
+
+ @endif +
+
+
+ +
+
+
+

Semua

{{ $currentPeriodForms->count() }}
+

Progress

{{ $currentPeriodForms->where('status', 'On Progress')->count() }}
+

App 1

{{ $currentPeriodForms->where('status', 'Approved 1')->count() }}
+

App 2

{{ $currentPeriodForms->where('status', 'Approved 2')->count() }}
+

Final

{{ $currentPeriodForms->where('status', 'Final Approved')->count() }}
+

Reject

{{ $currentPeriodForms->where('status', 'Rejected')->count() }}
+

Closed

{{ $currentPeriodForms->where('status', 'Closed')->count() }}
+
+
+
+
-

List Data Forms Entertainment & Presentation

+

List Data Forms Entertainment & Presentation

- @if (auth()->user()->can('forms.entertainment.create')) - - Add New Expense Entertainment & Presentation - - @endif + @if (auth()->user()->can('forms.entertainment.create')) + Add New Expense + @endif

-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if (auth()->user()->can('final_approval.view')) - - @endif - - - - - @php - use App\Helpers\NextCloudHelper; - $role = auth()->user()->getRoleNames()[0]; - @endphp - @foreach ($forms as $item) - - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if(auth()->user()->can('final_approval.view')) - - @endif - - - @endforeach - -
#No ExpenseUserCabangNama PenerimaTanggalJenisKeteranganTotalTotal ApprovedBuktiAccount NumberStatusApproval 1Approval 2Final ApprovalAksi
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->cabang->cabang->name }}{{ $item->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('D MMMM Y') }}{{ $item->jenis }}{{ $item->keterangan }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }} - @if ($item->bukti_total) - - Download - - @else - - - @endif - {{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif - - {{-- approve / reject button --}} - @if (auth()->user()->can('approval.approve')) - @if ($item->status == 'On Progress') -
- -
- - - @endif - @endif - @if ($item->approved_at) - {{ \Carbon\Carbon::parse($item->approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if (auth()->user()->can('approval2.approve')) - @if ($item->status == 'Approved 1' && $item->approved_at) -
- -
- - - @endif - @endif - @if ($item->approved2_at) - {{ \Carbon\Carbon::parse($item->approved2_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if(auth()->user()->can('final_approval.approve')) - @if ($item->status == 'Approved 2' && $item->approved2_at && !$item->final_approved_at) -
- -
- @else - @if ($item->final_approved_at && auth()->user()->can('final_approval.open')) -
- @csrf - @method('PUT') - -
- @endif - @endif - @endif - @if ($item->final_approved_at) - {{ \Carbon\Carbon::parse($item->final_approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- @if (auth()->user()->can('forms.entertainment.view')) - - - - @endif - - @php - $canEdit = false; - if (auth()->user()->can('forms.entertainment.edit')) { - if (in_array($role, ['Medical Representatif', 'Area Manager Cabang'])) { - $canEdit = $item->status == 'On Progress'; - } else { - $canEdit = true; - } - } - @endphp - @if ($canEdit) - - - - @endif - - @if (($role != 'Medical Representatif' && auth()->user()->can('forms.entertainment.delete')) || ($role == 'Medical Representatif' && $item->status == 'On Progress')) -
- @csrf - @method('DELETE') - -
- @endif -
-
-
+
+ + + + + + + + + + + + + + + + + @if (auth()->user()->can('approval.view')) @endif + @if (auth()->user()->can('approval2.view')) @endif + @if (auth()->user()->can('final_approval.view')) @endif + + + + + @php + // SUNTIKAN PERBAIKAN: Impor Class Helper NextCloud ke dalam scope Blade View + use App\Helpers\NextCloudHelper; + $role = auth()->user()->getRoleNames()[0]; + @endphp + @foreach ($forms as $item) + + + + + + + + + + + + + + + + @if (auth()->user()->can('approval.view')) + + @endif + @if (auth()->user()->can('approval2.view')) + + @endif + @if(auth()->user()->can('final_approval.view')) + + @endif + + + + @endforeach + +
#No ExpenseUserCabangNama PenerimaTanggalJenisKeteranganTotalTotal ApprovedBuktiAccount NumberStatusApproval 1Approval 2Final ApprovalAksi
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->cabang->cabang->name }}{{ $item->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('D MMMM Y') }}{{ $item->jenis }}{{ $item->keterangan }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }} + @if ($item->bukti_total) + Download + @else - @endif + {{ $item->account_number }} + @if ($item->status == 'On Progress') + {{ $item->status }} + @elseif (in_array($item->status, ['Approved 1', 'Approved 2', 'Final Approved'])) + {{ $item->status }} + @else + {{ $item->status }} + @endif + +
+ @if (auth()->user()->can('approval.approve') && $item->status == 'On Progress') + + + @endif +
+ @if ($item->approved_at) {{ \Carbon\Carbon::parse($item->approved_at)->format('d/m/y H:i') }} @endif +
+
+ @if (auth()->user()->can('approval2.approve') && $item->status == 'Approved 1' && $item->approved_at) + + + @endif +
+ @if ($item->approved2_at) {{ \Carbon\Carbon::parse($item->approved2_at)->format('d/m/y H:i') }} @endif +
+
+ @if(auth()->user()->can('final_approval.approve')) + @if ($item->status == 'Approved 2' && $item->approved2_at && !$item->final_approved_at) + + + @elseif ($item->final_approved_at && auth()->user()->can('final_approval.open')) +
+ @csrf @method('PUT') +
+ @endif + @endif +
+ @if ($item->final_approved_at) {{ \Carbon\Carbon::parse($item->final_approved_at)->format('d/m/y H:i') }} @endif +
+ @if (auth()->user()->can('forms.entertainment.view')) @endif + @php $canEdit = auth()->user()->can('forms.entertainment.edit') && (!in_array($role, ['Medical Representatif', 'Area Manager Cabang']) || $item->status == 'On Progress'); @endphp + @if ($canEdit) @endif + @if (($role != 'Medical Representatif' && auth()->user()->can('forms.entertainment.delete')) || ($role == 'Medical Representatif' && $item->status == 'On Progress')) +
+ @csrf @method('DELETE') +
+ @endif +
+
-
- -
-
- -
-
- -
+ @endsection @section('scripts') - -@endsection + $('#finalApproveModal').on('hidden.bs.modal', function () { $('.custom-control-input').prop('checked', false); $('#total').val('0'); }); + }); + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/entertainment/view.blade.php b/resources/views/backend/pages/forms/entertainment/view.blade.php index 9ee6b03..50c8b74 100644 --- a/resources/views/backend/pages/forms/entertainment/view.blade.php +++ b/resources/views/backend/pages/forms/entertainment/view.blade.php @@ -26,11 +26,11 @@
-
+
@include('backend.layouts.partials.messages')
-
+
@@ -55,6 +55,10 @@
+
+ + +
@@ -62,6 +66,14 @@
+
+ + +
+
+ + +
@@ -118,7 +130,7 @@
-
+
@@ -130,196 +142,210 @@ @if (auth()->user()->can('approval.approve') && $form->status == 'On Progress') - + @elseif(auth()->user()->can('approval2.approve') && $form->status == 'Approved 1') - + @elseif(auth()->user()->can('final_approval.approve') && $form->status == 'Approved 2') @endif
-
+
-
-
-
+
+ - -
-
- -
+ + + + + + +
+ +
@endsection @section('scripts') @@ -329,7 +355,7 @@ decimalCharacter: ',', // Karakter desimal (tidak digunakan karena tanpa desimal) currencySymbol: 'Rp.', // Simbol mata uang decimalPlaces: 0, // Tidak ada angka desimal - unformatOnSubmit: true // Nilai asli tanpa format saat dikirimkan + unformatOnSubmit: true // Nilai asli tanpa format saat dikirimkan }); @@ -351,131 +377,141 @@ }); $(document).on('click', '.open-approve-modal', function() { - const approveUrl = $(this).data('id'); + const approveUrl = $(this).data('id'); - // Show spinner and hide content initially - $('#loadingSpinner').show(); - $('#modalContent').addClass('d-none'); + // Show spinner and hide content initially + $('#loadingSpinner').show(); + $('#modalContent').addClass('d-none'); - $('#approveForm').attr('action', approveUrl); // Set the form action - $('#loadingSpinner').addClass('d-flex'); - $('#approveModal').modal('show'); // Show the modal + $('#approveForm').attr('action', approveUrl); // Set the form action + $('#loadingSpinner').addClass('d-flex'); + $('#approveModal').modal('show'); // Show the modal - // Get detail from /forms/entertainment/detail/{id} - $.get(approveUrl.replace(/approve2|final-approve/g, 'detail'), function (data) { - const formatter = new Intl.NumberFormat('id-ID', { - style: 'currency', - currency: 'IDR', - minimumFractionDigits: 0 - }); + // Get detail from /forms/entertainment/detail/{id} + $.get(approveUrl.replace(/approve2|final-approve/g, 'detail'), function (data) { + const formatter = new Intl.NumberFormat('id-ID', { + style: 'currency', + currency: 'IDR', + minimumFractionDigits: 0 + }); - $('#total_value').text(formatter.format(data.total)); + $('#total_value').text(formatter.format(data.total)); - // Detail data - $('#user').text(data.user.name); - $('#expense_number').text(data.expense_number); - $('#tanggal').text(new Date(data.created_at).toLocaleDateString('id-ID', { - year: 'numeric', - month: 'long', - day: 'numeric' - })); + // Detail data + $('#modal_user').text(data.user.name); + $('#modal_expense_number').text(data.expense_number); + $('#modal_tanggal').text(new Date(data.created_at).toLocaleDateString('id-ID', { + year: 'numeric', + month: 'long', + day: 'numeric' + })); - $('#nik_or_npwp').text(data.nik_or_npwp); - $('#name').text(data.name); - $('#jenis').text(data.jenis); - $('#keterangan').text(data.keterangan); + $('#modal_nik_or_npwp').text(data.nik_or_npwp); + + // Tambahan Mapping Data Perusahaan + $('#modal_nama_perusahaan').text(data.nama_perusahaan || '-'); + $('#modal_jabatan').text(data.jabatan || '-'); + $('#modal_jenis_usaha').text(data.jenis_usaha || '-'); + + $('#modal_name').text(data.name); + $('#modal_jenis').text(data.jenis); + $('#modal_keterangan').text(data.keterangan); - $('#bukti_total').html(data.total ? 'Download' : '-'); + $('#modal_bukti_total').html(data.total ? 'Download' : '-'); - // Hide spinner and show content - $('#loadingSpinner').hide(); - $('#loadingSpinner').removeClass('d-flex'); - $('#loadingSpinner').addClass('d-none'); - $('#modalContent').removeClass('d-none'); + // Hide spinner and show content + $('#loadingSpinner').hide(); + $('#loadingSpinner').removeClass('d-flex'); + $('#loadingSpinner').addClass('d-none'); + $('#modalContent').removeClass('d-none'); - // Reset the total to zero - $('#total').val(0); - }); - }); + // Reset the total to zero + $('#total').val(0); + }); + }); - // Handle Approve Button Click - $(document).on('click', '.final-approve-modal', function() { - const approveUrl = $(this).data('id'); + // Handle Approve Button Click + $(document).on('click', '.final-approve-modal', function() { + const approveUrl = $(this).data('id'); - // Show spinner and hide content initially - $('#finalLoadingSpinner').show(); - $('#finalModalContent').addClass('d-none'); + // Show spinner and hide content initially + $('#finalLoadingSpinner').show(); + $('#finalModalContent').addClass('d-none'); - $('#finalApproveForm').attr('action', approveUrl); // Set the form action - $('#finalLoadingSpinner').addClass('d-flex'); - $('#finalApproveModal').modal('show'); // Show the modal + $('#finalApproveForm').attr('action', approveUrl); // Set the form action + $('#finalLoadingSpinner').addClass('d-flex'); + $('#finalApproveModal').modal('show'); // Show the modal - // Get detail from /forms/entertainment/detail/{id} - $.get(approveUrl.replace('approve', 'detail'), function (data) { - console.log(data); - const formatter = new Intl.NumberFormat('id-ID', { - style: 'currency', - currency: 'IDR', - minimumFractionDigits: 0 - }); + // Get detail from /forms/entertainment/detail/{id} + $.get(approveUrl.replace('approve', 'detail'), function (data) { + console.log(data); + const formatter = new Intl.NumberFormat('id-ID', { + style: 'currency', + currency: 'IDR', + minimumFractionDigits: 0 + }); - $('#final_total_value').text(formatter.format(data.total)); + $('#final_total_value').text(formatter.format(data.total)); - // Assign data values to checkboxes - $('#final_total').data('value', data.total); + // Assign data values to checkboxes + $('#final_total').data('value', data.total); - // Detail data - $('#final_user').text(data.user.name); - $('#final_expense_number').text(data.expense_number); - $('#final_tanggal').text(new Date(data.created_at).toLocaleDateString('id-ID', { - year: 'numeric', - month: 'long', - day: 'numeric' - })); + // Detail data + $('#final_user').text(data.user.name); + $('#final_expense_number').text(data.expense_number); + $('#final_tanggal').text(new Date(data.created_at).toLocaleDateString('id-ID', { + year: 'numeric', + month: 'long', + day: 'numeric' + })); - $('#final_nik_or_npwp').text(data.nik_or_npwp); - $('#final_name').text(data.name); - $('#final_jenis').text(data.jenis); - $('#final_keterangan').text(data.keterangan); + $('#final_nik_or_npwp').text(data.nik_or_npwp); + + // Tambahan Mapping Data Perusahaan + $('#final_nama_perusahaan').text(data.nama_perusahaan || '-'); + $('#final_jabatan').text(data.jabatan || '-'); + $('#final_jenis_usaha').text(data.jenis_usaha || '-'); + + $('#final_name').text(data.name); + $('#final_jenis').text(data.jenis); + $('#final_keterangan').text(data.keterangan); + $('#final_bukti_total').html(data.bukti_total ? 'Download' : '-'); - $('#final_bukti_total').html(data.bukti_total ? 'Download' : '-'); + // Hide spinner and show content + $('#finalLoadingSpinner').hide(); + $('#finalLoadingSpinner').removeClass('d-flex'); + $('#finalLoadingSpinner').addClass('d-none'); + $('#finalModalContent').removeClass('d-none'); - // Hide spinner and show content - $('#finalLoadingSpinner').hide(); - $('#finalLoadingSpinner').removeClass('d-flex'); - $('#finalLoadingSpinner').addClass('d-none'); - $('#finalModalContent').removeClass('d-none'); + // Reset the total to zero + $('#total').val(0); + }); + }); - // Reset the total to zero - $('#total').val(0); - }); - }); + // Update total dynamically + $('.custom-control-input').on('change', function () { + let total = 0; + $('.custom-control-input:checked').each(function () { + total += parseFloat($(this).data('value') || 0); + }); - // Update total dynamically - $('.custom-control-input').on('change', function () { - let total = 0; - $('.custom-control-input:checked').each(function () { - total += parseFloat($(this).data('value') || 0); - }); + const formatter = new Intl.NumberFormat('id-ID', { + style: 'currency', + currency: 'IDR', + minimumFractionDigits: 0, + }); - const formatter = new Intl.NumberFormat('id-ID', { - style: 'currency', - currency: 'IDR', - minimumFractionDigits: 0, - }); + $('#total').val(formatter.format(total)); + }); - $('#total').val(formatter.format(total)); - }); - - - // Uncheck all checkboxes when the modal is closed - $('#finalApproveModal').on('hidden.bs.modal', function () { - $('.custom-control-input').prop('checked', false); // Uncheck all checkboxes - $('#total').val('0'); // Reset total to 0 - }); + // Uncheck all checkboxes when the modal is closed + $('#finalApproveModal').on('hidden.bs.modal', function () { + $('.custom-control-input').prop('checked', false); // Uncheck all checkboxes + $('#total').val('0'); // Reset total to 0 + }); @include('backend.pages.forms.entertainment.partials.attachment-modal') @include('backend.pages.forms.entertainment.partials.attachment-scripts') -@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/meeting/index.blade.php b/resources/views/backend/pages/forms/meeting/index.blade.php index dc19ea5..e217149 100644 --- a/resources/views/backend/pages/forms/meeting/index.blade.php +++ b/resources/views/backend/pages/forms/meeting/index.blade.php @@ -1,21 +1,77 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') + @php $outstandingIdCollection = collect($outstandingIds ?? []); + + // ========================================== + // FILTER PERIODE BERJALAN KHUSUS UNTUK MINI CARDS + // (Agar Angka sinkron dengan tabel filter JS di bawah) + // ========================================== + $startDayIndex = (int) env('STARTING_DATE', 11); + $closingDayIndex = (int) env('CLOSING_DATE', 13); + $nowIndex = \Carbon\Carbon::now(); + + if ($nowIndex->day >= $startDayIndex) { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->addMonth()->year, $nowIndex->copy()->addMonth()->month, $closingDayIndex)->endOfDay(); + } else { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->subMonth()->year, $nowIndex->copy()->subMonth()->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $closingDayIndex)->endOfDay(); + } + + // Penampung data pengajuan MURNI HANYA DI PERIODE BERJALAN SAAT INI + $currentPeriodForms = $forms->filter(function($form) use ($startPeriodIndex, $endPeriodIndex) { + $tanggalForm = \Carbon\Carbon::parse($form->tanggal); + return $tanggalForm->between($startPeriodIndex, $endPeriodIndex); + }); @endphp +
@@ -25,670 +81,438 @@
-
-
-
- -
-
-
-

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

-

Total Expense

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'), 0, ',', '.') }}

-

Approved Expenses

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

-

Pending Expenses

-
-
- -
-
-
- -
-
-
-

{{ $outstandingCount }}

-

Outstanding Expenses

-

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

-

Klik untuk menampilkan detail

-
-
- -
-
-
- @if($availableBudget > 0) -
-
-
-

Rp {{ number_format($availableBudget, 0, ',', '.') }}

-

Available Budget

-
-
- -
-
-
- @endif -
-
-
-
+ +
+
+
+
+
+
+

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

+

Total Expense

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'), 0, ',', '.') }}

+

Approved Expenses

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

+

Pending Expenses

+
+
+
+
+
+
+
+

{{ $outstandingCount }}

+

Outstanding Expenses

+

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

+

Klik untuk menampilkan detail

+
+
+
+
+ @if($availableBudget > 0) +
+
+
+

Rp {{ number_format($availableBudget, 0, ',', '.') }}

+

Available Budget

+
+
+
+
+ @endif +
+
+
+ +
+
+
+

Semua

{{ $currentPeriodForms->count() }}
+

Progress

{{ $currentPeriodForms->where('status', 'On Progress')->count() }}
+

App 1

{{ $currentPeriodForms->where('status', 'Approved 1')->count() }}
+

App 2

{{ $currentPeriodForms->where('status', 'Approved 2')->count() }}
+

Final

{{ $currentPeriodForms->where('status', 'Final Approved')->count() }}
+

Reject

{{ $currentPeriodForms->where('status', 'Rejected')->count() }}
+

Closed

{{ $currentPeriodForms->where('status', 'Closed')->count() }}
+
+
+
+ +
-

List Data Forms Meeting Seminar Anda

+

List Data Forms Meeting Seminar Anda

- @if (auth()->user()->can('forms.meeting.create')) - - Add New Expense Meeting Seminar - - @endif + @if (auth()->user()->can('forms.meeting.create')) + Add New Expense + @endif

@include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if (auth()->user()->can('final_approval.view')) - - @endif - - - - - @php - use App\Helpers\NextCloudHelper; - $role = auth()->user()->getRoleNames()[0]; - @endphp - @foreach ($forms as $item) - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if(auth()->user()->can('final_approval.view')) - - @endif - - - @endforeach - -
#No ExpenseNamaCabangTanggalAllowanceTransport AnkotHotelTotalTotal ApprovedAccount NumberStatusApproval 1Approval 2Final ApprovalAksi
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->cabang->cabang->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('D MMMM Y') }}{{ number_format($item->allowance, 0, ',', '.') }}{{ number_format($item->transport_ankot, 0, ',', '.') }}{{ number_format($item->hotel, 0, ',', '.') }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }}{{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif - - {{-- approve / reject button --}} - @if (auth()->user()->can('approval.approve')) - @if ($item->status == 'On Progress') -
- -
- - - @endif - @endif - @if ($item->approved_at) - {{ \Carbon\Carbon::parse($item->approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if (auth()->user()->can('approval2.approve')) - @if ($item->status == 'Approved 1' && $item->approved_at) -
- -
- - - @endif - @endif - @if ($item->approved2_at) - {{ \Carbon\Carbon::parse($item->approved2_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if(auth()->user()->can('final_approval.approve')) - @if ($item->status == 'Approved 2' && $item->approved2_at && !$item->final_approved_at) -
- -
- @else - @if ($item->final_approved_at && auth()->user()->can('final_approval.open')) -
- @csrf - @method('PUT') - -
- @endif - @endif - @endif - @if ($item->final_approved_at) - {{ \Carbon\Carbon::parse($item->final_approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- @if (auth()->user()->can('forms.meeting.view')) - - - - @endif - - @php - $canEdit = false; - if (auth()->user()->can('forms.meeting.edit')) { - if (in_array($role, ['Medical Representatif', 'Area Manager Cabang'])) { - $canEdit = $item->status == 'On Progress'; - } else { - $canEdit = true; - } - } - @endphp - @if ($canEdit) - - - - @endif - - @if (($role != 'Medical Representatif' && auth()->user()->can('forms.meeting.delete')) || ($role == 'Medical Representatif' && $item->status == 'On Progress')) -
- @csrf - @method('DELETE') - -
- @endif -
-
-
+
+ + + + + + + + + + + + + + + + @if (auth()->user()->can('approval.view')) @endif + @if (auth()->user()->can('approval2.view')) @endif + @if (auth()->user()->can('final_approval.view')) @endif + + + + + @php + use App\Helpers\NextCloudHelper; + $role = auth()->user()->getRoleNames()[0]; + @endphp + @foreach ($forms as $item) + + + + + + + + + + + + + + + {{-- SELEKTIF MENYEMBUNYIKAN TOMBOL APPROVE/REJECT DIRECT DI INDEX TANPA MENGHAPUS LOGIKA KODE --}} + @if (auth()->user()->can('approval.view')) + + @endif + @if (auth()->user()->can('approval2.view')) + + @endif + @if(auth()->user()->can('final_approval.view')) + + @endif + + + + @endforeach + +
#No ExpenseNamaCabangTanggalAllowanceTransport AnkotHotelTotalTotal ApprovedAccount NumberStatusApproval 1Approval 2Final ApprovalAksi
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->cabang->cabang->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('dddd, D MMMM Y HH:mm:ss') }}{{ number_format($item->allowance, 0, ',', '.') }}{{ number_format($item->transport_ankot, 0, ',', '.') }}{{ number_format($item->hotel, 0, ',', '.') }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }}{{ $item->account_number }} + @if ($item->status == 'On Progress') + {{ $item->status }} + @elseif (in_array($item->status, ['Approved 1', 'Approved 2', 'Final Approved'])) + {{ $item->status }} + @else + {{ $item->status }} + @endif + +
+ @if (auth()->user()->can('approval.approve') && $item->status == 'On Progress') + + + @endif +
+ @if ($item->approved_at)
{{ \Carbon\Carbon::parse($item->approved_at)->format('d/m/y H:i') }} @endif +
+
+ @if (auth()->user()->can('approval2.approve') && $item->status == 'Approved 1' && $item->approved_at) + + + @endif +
+ @if ($item->approved2_at)
{{ \Carbon\Carbon::parse($item->approved2_at)->format('d/m/y H:i') }} @endif +
+
+ @if(auth()->user()->can('final_approval.approve')) + @if ($item->status == 'Approved 2' && $item->approved2_at && !$item->final_approved_at) + + + @elseif ($item->final_approved_at && auth()->user()->can('final_approval.open')) +
+ @csrf @method('PUT') +
+ @endif + @endif +
+ @if ($item->final_approved_at)
{{ \Carbon\Carbon::parse($item->final_approved_at)->format('d/m/y H:i') }} @endif +
+ @if (auth()->user()->can('forms.meeting.view')) + + @endif + @php + $canEdit = auth()->user()->can('forms.meeting.edit') && (!in_array($role, ['Medical Representatif', 'Area Manager Cabang']) || $item->status == 'On Progress'); + @endphp + @if ($canEdit) + + @endif + @if (($role != 'Medical Representatif' && auth()->user()->can('forms.meeting.delete')) || ($role == 'Medical Representatif' && $item->status == 'On Progress')) +
+ @csrf @method('DELETE') +
+ @endif +
+
+
-
- -
-
- -
-
- -
+ @endsection @section('scripts') - -@endsection + $('#finalApproveModal').on('hidden.bs.modal', function () { + $('.custom-control-input').prop('checked', false); $('#total').val('0'); + }); + }); + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/other/create.blade.php b/resources/views/backend/pages/forms/other/create.blade.php index 684d585..43e7ca6 100644 --- a/resources/views/backend/pages/forms/other/create.blade.php +++ b/resources/views/backend/pages/forms/other/create.blade.php @@ -1,11 +1,12 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') +
@@ -45,7 +46,7 @@
-
+ @csrf @include('backend.layouts.partials.messages')
@@ -82,7 +83,7 @@
-
@@ -113,7 +114,7 @@
-
+ @include('backend.components.attachment-preview-modal', [ 'modalId' => 'otherAttachmentPreviewModal', @@ -133,19 +134,44 @@
-@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/other/edit.blade.php b/resources/views/backend/pages/forms/other/edit.blade.php index 5a49e79..5e2cc39 100644 --- a/resources/views/backend/pages/forms/other/edit.blade.php +++ b/resources/views/backend/pages/forms/other/edit.blade.php @@ -1,7 +1,7 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') @@ -45,7 +45,7 @@
-
+ @csrf @method('PUT') @include('backend.layouts.partials.messages') @@ -169,7 +169,7 @@
- + @include('backend.components.attachment-preview-modal', [ 'modalId' => 'otherExistingAttachmentPreviewModal', @@ -195,18 +195,44 @@ -@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/other/index.blade.php b/resources/views/backend/pages/forms/other/index.blade.php index 15fedc6..37273ef 100644 --- a/resources/views/backend/pages/forms/other/index.blade.php +++ b/resources/views/backend/pages/forms/other/index.blade.php @@ -1,21 +1,77 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') + @php $outstandingIdCollection = collect($outstandingIds ?? []); + + // ========================================== + // FILTER PERIODE BERJALAN KHUSUS UNTUK MINI CARDS + // (Sinkronisasi Angka Realtime Siklus Tanggal 11-13) + // ========================================== + $startDayIndex = (int) env('STARTING_DATE', 11); + $closingDayIndex = (int) env('CLOSING_DATE', 13); + $nowIndex = \Carbon\Carbon::now(); + + if ($nowIndex->day >= $startDayIndex) { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->addMonth()->year, $nowIndex->copy()->addMonth()->month, $closingDayIndex)->endOfDay(); + } else { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->subMonth()->year, $nowIndex->copy()->subMonth()->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $closingDayIndex)->endOfDay(); + } + + // Koleksi data pengajuan MURNI HANYA DI PERIODE BERJALAN AKTIF (11 s/d 13) + $currentPeriodForms = $forms->filter(function($form) use ($startPeriodIndex, $endPeriodIndex) { + $tanggalForm = \Carbon\Carbon::parse($form->tanggal); + return $tanggalForm->between($startPeriodIndex, $endPeriodIndex); + }); @endphp +
@@ -25,640 +81,519 @@
-
-
-
- -
-
-
-

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

-

Total Expense

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'), 0, ',', '.') }}

-

Approved Expenses

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

-

Pending Expenses

-
-
- -
-
-
- -
-
-
-

{{ $outstandingCount }}

-

Outstanding Expenses

-

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

-

Klik untuk menampilkan detail

-
-
- -
-
-
- @if($availableBudget > 0) -
-
-
-

Rp {{ number_format($availableBudget, 0, ',', '.') }}

-

Available Budget

-
-
- -
-
-
- @endif -
-
-
+
-
-
-

List Data Forms Others Anda

-

- @if (auth()->user()->can('forms.other.create')) - - Add New Expense Others - - @endif -

-
-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if (auth()->user()->can('final_approval.view')) - - @endif - - - - - @php - use App\Helpers\NextCloudHelper; - $role = auth()->user()->getRoleNames()[0]; - @endphp - @foreach ($forms as $item) - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if(auth()->user()->can('final_approval.view')) - - @endif - - - @endforeach - -
#No ExpenseNamaCabangTanggalJenisKeteranganTotalTotal ApprovedBukti TotalAccount NumberStatusApproval 1Approval 2Final ApprovalAksi
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->cabang->cabang->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('D MMMM Y') }}{{ $item->kategori->name }}{{ $item->keterangan }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }} - @if ($item->bukti_total) - - Download - - @else - - - @endif - {{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif - - {{-- approve / reject button --}} - @if (auth()->user()->can('approval.approve')) - @if ($item->status == 'On Progress') -
- -
- - - @endif - @endif - @if ($item->approved_at) - {{ \Carbon\Carbon::parse($item->approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if (auth()->user()->can('approval2.approve')) - @if ($item->status == 'Approved 1' && $item->approved_at) -
- -
+
+
+
+
+

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

+

Total Expense

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed', 'Final Approved'])->sum('approved_total'), 0, ',', '.') }}

+

Approved Expenses

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

+

Pending Expenses

+
+
+
+
+
+
+
+

{{ $outstandingCount }}

+

Outstanding Expenses

+

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

+

Klik untuk menampilkan detail

+
+
+
+
+ @if($availableBudget > 0) +
+
+
+

Rp {{ number_format($availableBudget, 0, ',', '.') }}

+

Available Budget

+
+
+
+
+ @endif +
+ + - - @endif - @endif - @if ($item->approved2_at) - {{ \Carbon\Carbon::parse($item->approved2_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if(auth()->user()->can('final_approval.approve')) - @if ($item->status == 'Approved 2' && $item->approved2_at && !$item->final_approved_at) -
- -
- @else - @if ($item->final_approved_at && auth()->user()->can('final_approval.open')) -
- @csrf - @method('PUT') - -
- @endif - @endif - @endif - @if ($item->final_approved_at) - {{ \Carbon\Carbon::parse($item->final_approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- @if (auth()->user()->can('forms.other.view')) - - - - @endif - - @php - $canEdit = false; - if (auth()->user()->can('forms.other.edit')) { - if (in_array($role, ['Medical Representatif', 'Area Manager Cabang'])) { - $canEdit = $item->status == 'On Progress'; - } else { - $canEdit = true; - } - } - @endphp - @if ($canEdit) - - - - @endif - - @if (($role != 'Medical Representatif' && auth()->user()->can('forms.other.delete')) || ($role == 'Medical Representatif' && $item->status == 'On Progress')) -
- @csrf - @method('DELETE') - -
- @endif -
-
-
+
+
+
+
+
+
+

Semua

{{ $currentPeriodForms->count() }}
+ +
+
+
+
+
+
+

Progress

{{ $currentPeriodForms->where('status', 'On Progress')->count() }}
+ +
+
+
+
+
+
+

App 1

{{ $currentPeriodForms->where('status', 'Approved 1')->count() }}
+ +
+
+
+
+
+
+

App 2

{{ $currentPeriodForms->where('status', 'Approved 2')->count() }}
+ +
+
+
+
+
+
+

Final

{{ $currentPeriodForms->where('status', 'Final Approved')->count() }}
+ +
+
+
+
+
+
+

Reject

{{ $currentPeriodForms->where('status', 'Rejected')->count() }}
+ +
+
+
+
+
+
+

Closed

{{ $currentPeriodForms->where('status', 'Closed')->count() }}
+ +
+
-
- -
-
- -
-
- -
+ @endsection @section('scripts') - -@endsection + $('#finalApproveModal').on('hidden.bs.modal', function () { + $('.custom-control-input').prop('checked', false); $('#total').val('0'); + }); + }); + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/upcountry/create.blade.php b/resources/views/backend/pages/forms/upcountry/create.blade.php index 1f78989..fdb3888 100644 --- a/resources/views/backend/pages/forms/upcountry/create.blade.php +++ b/resources/views/backend/pages/forms/upcountry/create.blade.php @@ -1,11 +1,13 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') + +
@@ -15,12 +17,12 @@
+ + @php $attachmentCategoryLabels = $attachmentCategoryLabels ?? ($upCountryAttachmentCategories ?? []); $attachmentCategories = $attachmentCategories ?? ($upCountryAttachmentCategoryKeys ?? array_keys($attachmentCategoryLabels)); @endphp +
-
+ @csrf @include('backend.layouts.partials.messages')
@@ -78,22 +82,22 @@
- +
- +
- +
- +
@@ -137,7 +141,7 @@
- + @include('backend.components.attachment-preview-modal', [ 'modalId' => 'newAttachmentPreviewModal', @@ -146,256 +150,284 @@
-
-
-

Sedang mengirim, mohon tunggu...

+
+

Sedang mengirim, mohon tunggu...

+@endsection +@section('scripts') -@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/upcountry/edit.blade.php b/resources/views/backend/pages/forms/upcountry/edit.blade.php index 5891852..d752041 100644 --- a/resources/views/backend/pages/forms/upcountry/edit.blade.php +++ b/resources/views/backend/pages/forms/upcountry/edit.blade.php @@ -1,12 +1,13 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') - + +
@@ -16,12 +17,12 @@
+ + @php $attachmentCategoryLabels = $attachmentCategoryLabels ?? ($upCountryAttachmentCategories ?? []); $attachmentCategories = $attachmentCategories ?? ($upCountryAttachmentCategoryKeys ?? array_keys($attachmentCategoryLabels)); @endphp +
-
+ @csrf @method('PUT') @include('backend.layouts.partials.messages') @@ -68,7 +71,8 @@
- +
@@ -80,22 +84,22 @@
- +
- +
- +
- +
@@ -201,7 +205,7 @@
- + @include('backend.components.attachment-preview-modal', [ 'modalId' => 'existingAttachmentPreviewModal', @@ -217,7 +221,7 @@
-

Sedang mengirim, mohon tunggu...

+

Sedang mengirim, mohon tunggu...

@@ -226,317 +230,333 @@
-@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/upcountry/index.blade.php b/resources/views/backend/pages/forms/upcountry/index.blade.php index 2dd123c..3bb0e00 100644 --- a/resources/views/backend/pages/forms/upcountry/index.blade.php +++ b/resources/views/backend/pages/forms/upcountry/index.blade.php @@ -1,21 +1,77 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') - - @php - $outstandingIdCollection = collect($outstandingIds ?? []); - @endphp + + + @php + $outstandingIdCollection = collect($outstandingIds ?? []); + + // ========================================== + // FILTER PERIODE BERJALAN KHUSUS UNTUK MINI CARDS + // (Agar Angka 30 berubah jadi 2 secara riil) + // ========================================== + $startDayIndex = (int) env('STARTING_DATE', 11); + $closingDayIndex = (int) env('CLOSING_DATE', 13); + $nowIndex = \Carbon\Carbon::now(); + + if ($nowIndex->day >= $startDayIndex) { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->addMonth()->year, $nowIndex->copy()->addMonth()->month, $closingDayIndex)->endOfDay(); + } else { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->subMonth()->year, $nowIndex->copy()->subMonth()->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $closingDayIndex)->endOfDay(); + } + + // Variabel baru penampung data pengajuan MURNI HANYA DI PERIODE BERJALAN SAAT INI + $currentPeriodForms = $forms->filter(function($form) use ($startPeriodIndex, $endPeriodIndex) { + $tanggalForm = \Carbon\Carbon::parse($form->tanggal); + return $tanggalForm->between($startPeriodIndex, $endPeriodIndex); + }); + @endphp +
@@ -25,753 +81,572 @@
-
-
-
- -
-
-
-

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

-

Total Expense

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'), 0, ',', '.') }}

-

Approved Expenses

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

-

Pending Expenses

-
-
- -
-
-
- -
-
-
-

{{ $outstandingCount }}

-

Outstanding Expenses

-

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

-

Klik untuk menampilkan detail

-
-
- -
-
-
- @if($availableBudget > 0) -
-
-
-

Rp {{ number_format($availableBudget, 0, ',', '.') }}

-

Available Budget

-
-
- -
-
-
- @endif -
-
-
+
-
-
-

List Data Forms Up Country Anda

-

- @if (auth()->user()->can('forms.country.create')) - - Add New Expense Up Country - - @endif -

-
-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if (auth()->user()->can('final_approval.view')) - - @endif - - - - - @php - use App\Helpers\NextCloudHelper; - $role = auth()->user()->getRoleNames()[0]; - @endphp - @foreach ($forms as $item) - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if(auth()->user()->can('final_approval.view')) - - @endif - - - @endforeach - -
#No ExpenseNamaCabangRayonTanggalTujuanJarakTotalTotal ApprovedAccount NumberStatusApproval 1Approval 2Final ApprovalAksi
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->cabang->cabang->name }}{{ $item->rayon->code }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('D MMMM Y') }}{{ $item->tujuan }}{{ $item->jarak }} km{{ number_format($item->total, 0, ',', '.') }} - @if ($item->approved_total) - {{ number_format($item->approved_total, 0, ',', '.') }} - @else - - - @endif - {{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif - - {{-- approve / reject button --}} - @if (auth()->user()->can('approval.approve')) - @if ($item->status == 'On Progress') -
- -
- - - @endif - @endif - @if ($item->approved_at) - {{ \Carbon\Carbon::parse($item->approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if (auth()->user()->can('approval2.approve')) - @if ($item->status == 'Approved 1' && $item->approved_at) -
- -
+
+
+
+
+

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

+

Total Expense

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed', 'Final Approved'])->sum('approved_total'), 0, ',', '.') }}

+

Approved Expenses

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

+

Pending Expenses

+
+
+
+
+
+
+
+

{{ $outstandingCount }}

+

Outstanding Expenses

+

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

+

Klik untuk menampilkan detail

+
+
+
+
+ @if($availableBudget > 0) +
+
+
+

Rp {{ number_format($availableBudget, 0, ',', '.') }}

+

Available Budget

+
+
+
+
+ @endif +
+ + - - @endif - @endif - @if ($item->approved2_at) - {{ \Carbon\Carbon::parse($item->approved2_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if(auth()->user()->can('final_approval.approve')) - @if ($item->status == 'Approved 2' && $item->approved2_at && !$item->final_approved_at) -
- -
- @else - @if ($item->final_approved_at && auth()->user()->can('final_approval.open')) -
- @csrf - @method('PUT') - -
- @endif - @endif - @endif - @if ($item->final_approved_at) - {{ \Carbon\Carbon::parse($item->final_approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- @if (auth()->user()->can('forms.country.view')) - - - - @endif - - @php - $canEdit = false; - if (auth()->user()->can('forms.country.edit')) { - if (in_array($role, ['Medical Representatif', 'Area Manager Cabang'])) { - $canEdit = $item->status == 'On Progress'; - } else { - $canEdit = true; - } - } - @endphp - @if ($canEdit) - - - - @endif - - @if (($role != 'Medical Representatif' && auth()->user()->can('forms.country.delete')) || ($role == 'Medical Representatif' && $item->status == 'On Progress')) -
- @csrf - @method('DELETE') - -
- @endif -
-
-
+
+
+
+
+
+
+ {{-- Inject variable $currentPeriodForms ke semua card agar sinkron --}} +

Semua

{{ $currentPeriodForms->count() }}
+ +
+
+
+
+
+
+

Progress

{{ $currentPeriodForms->where('status', 'On Progress')->count() }}
+ +
+
+
+
+
+
+

App 1

{{ $currentPeriodForms->where('status', 'Approved 1')->count() }}
+ +
+
+
+
+
+
+

App 2

{{ $currentPeriodForms->where('status', 'Approved 2')->count() }}
+ +
+
+
+
+
+
+

Final

{{ $currentPeriodForms->where('status', 'Final Approved')->count() }}
+ +
+
+
+
+
+
+

Reject

{{ $currentPeriodForms->where('status', 'Rejected')->count() }}
+ +
+
+
+
+
+
+

Closed

{{ $currentPeriodForms->where('status', 'Closed')->count() }}
+ +
+
-
- -
-
- -
-
- -
+ @endsection @section('scripts') - -@endsection + $('#finalApproveModal').on('hidden.bs.modal', function () { + $('.custom-control-input').prop('checked', false); + $('#total').val('0'); + if($('#hidden_approved_total').length) { + $('#hidden_approved_total').remove(); + } + }); + }); + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/upcountry/view.blade.php b/resources/views/backend/pages/forms/upcountry/view.blade.php index 1427f93..9865cd8 100644 --- a/resources/views/backend/pages/forms/upcountry/view.blade.php +++ b/resources/views/backend/pages/forms/upcountry/view.blade.php @@ -1,11 +1,13 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') + +
@@ -15,17 +17,17 @@
+
-
+
@include('backend.layouts.partials.messages')
@@ -43,8 +45,9 @@
- - + +
@@ -56,41 +59,44 @@
- +
-
+
- +
-
+
- -
+ +
-
+
- +
-
+
-
+
-
-
-
- Lihat Bukti -
- +
+
+
+ @if($form->uc_plan) + Lihat Dokumen Plan + @else + - Tidak ada plan diunggah - + @endif +
- +
@@ -130,7 +136,7 @@ @empty - + @endforelse @@ -143,311 +149,443 @@ Kembali @if (auth()->user()->can('approval.approve') && $form->status == 'On Progress') - - + @elseif(auth()->user()->can('approval2.approve') && $form->status == 'Approved 1') - - + @elseif(auth()->user()->can('final_approval.approve') && $form->status == 'Approved 2') @endif - + + + {{-- MODAL PREVIEW DAN APPROVAL SYSTEM --}}
- @include('backend.components.attachment-preview-modal', [ + @include('backend.components.attachment-preview-modal', [ 'modalId' => 'viewAttachmentPreviewModal', 'title' => 'Attachment Preview', ]) -
-
-
Tidak ada lampiran.Tidak ada lampiran bukti tersedia.
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Komponen BiayaNominal PengajuanLink File Bukti
Allowance:
Transport Dalam Kota (Dalkot):
Transport Antar Kota (Ankot):
Hotel:
UC Plan Dokumen:-
+
+
+
+ + +
+
+
+
- - -
-
-
-
-
- -
+
+ {{-- MODAL CHECKLIST UNTUK FINAL APPROVE --}} + +
+ +
+ +
@endsection @section('scripts') + // Logika Pembukaan Modal Reject + $(document).on('click', '.open-reject-modal', function () { + const rejectUrl = $(this).data('action'); + $('#rejectForm').attr('action', rejectUrl); + $('#rejectRemarks').val(''); + $('#rejectModal').modal('show'); + }); - -@endsection - +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/vehicle/create.blade.php b/resources/views/backend/pages/forms/vehicle/create.blade.php index b824df4..fb3c93c 100644 --- a/resources/views/backend/pages/forms/vehicle/create.blade.php +++ b/resources/views/backend/pages/forms/vehicle/create.blade.php @@ -6,6 +6,7 @@ @section('admin-content') +
@@ -28,8 +29,8 @@ left: 0; width: 100%; height: 100%; - background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black background */ - z-index: 9999; /* High z-index to cover everything */ + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; display: flex; justify-content: center; align-items: center; @@ -51,7 +52,8 @@
- + {{-- Mengubah datetime-local menjadi date --}} +
@@ -63,11 +65,11 @@
- +
- +
@@ -78,7 +80,8 @@
- + @@ -135,9 +138,8 @@
-
-
-

Submitting, please wait...

+
+

Submitting, please wait...

@@ -146,226 +148,211 @@
-@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/vehicle/edit.blade.php b/resources/views/backend/pages/forms/vehicle/edit.blade.php index e3d24ee..0637065 100644 --- a/resources/views/backend/pages/forms/vehicle/edit.blade.php +++ b/resources/views/backend/pages/forms/vehicle/edit.blade.php @@ -6,6 +6,8 @@ @section('admin-content') + +
@@ -27,8 +29,8 @@ left: 0; width: 100%; height: 100%; - background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent black background */ - z-index: 9999; /* High z-index to cover everything */ + background-color: rgba(0, 0, 0, 0.5); + z-index: 9999; display: flex; justify-content: center; align-items: center; @@ -39,6 +41,12 @@ color: white; } + + @php + $attachmentCategoryLabels = $attachmentCategoryLabels ?? ($vehicleAttachmentCategories ?? []); + $attachmentCategories = $attachmentCategories ?? ($vehicleAttachmentCategoryKeys ?? array_keys($attachmentCategoryLabels)); + @endphp +
@@ -51,9 +59,10 @@
- + {{-- Konversi Cerdas Format Input Tipe 'date' --}} +
- {{-- Updated: Expense Type dropdown with name="type" and $form->type for selection --}}
- {{-- Wrap gasoline-specific fields with .gasoline-fields --}}
- +
- +
- {{-- Wrap gasoline-specific fields with .gasoline-fields --}}
@@ -82,6 +89,7 @@
@@ -96,53 +104,61 @@
-
+ {{-- BAGIAN LAMPIRAN TERSIMPAN DATABASE --}} +
- -

Gunakan tombol preview untuk melihat lampiran.

+ +

Pratinjau, unduh, atau hapus lampiran yang telah diunggah sebelumnya.

- +
- - - - + + + + + @forelse ($attachments as $attachment) - - + @php + $categoryValue = $attachment['file_category'] ?? null; + $categoryLabel = $categoryValue + ? ($attachmentCategoryLabels[$categoryValue] ?? ucwords(str_replace('_', ' ', $categoryValue))) + : '-'; + @endphp + + + @empty - - + + @endforelse @@ -155,15 +171,15 @@
-

Maksimal 10 MB per file. Semua tipe file diperbolehkan kecuali .exe, .bat, .sh, .cmd, .dll, dan .msi.

-
KategoriNama FilePreviewAksiKategori LampiranNama FilePratinjauUnduhAksi
{{ $attachment['file_category'] ? ucwords(str_replace('_', ' ', $attachment['file_category'])) : '-' }}
{{ $categoryLabel }} {{ $attachment['filename'] ?? basename($attachment['file_path']) }} - Download + class="btn btn-sm btn-outline-primary"> + Unduh - @if($attachment['can_delete']) - - @endif + +
Belum ada lampiran.
Belum ada lampiran tersimpan.
+
@@ -173,8 +189,8 @@ - - + +
Kategori
Belum ada lampiran baru.
Belum ada lampiran.
@@ -182,13 +198,13 @@
- +
@include('backend.components.attachment-preview-modal', [ - 'modalId' => 'vehicleExistingAttachmentPreviewModal', - 'title' => 'Preview Lampiran', + 'modalId' => 'existingAttachmentPreviewModal', + 'title' => 'Pratinjau Lampiran', ]) @include('backend.components.attachment-preview-modal', [ @@ -198,9 +214,8 @@
-
-
-

Submitting, please wait...

+
+

Submitting, please wait...

@@ -208,255 +223,280 @@
- -@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/forms/vehicle/index.blade.php b/resources/views/backend/pages/forms/vehicle/index.blade.php index b8186a4..90b716f 100644 --- a/resources/views/backend/pages/forms/vehicle/index.blade.php +++ b/resources/views/backend/pages/forms/vehicle/index.blade.php @@ -1,21 +1,77 @@ @extends('layouts.app') @section('title') - Dashboard + Dashboard @endsection @section('admin-content') - - @php - $outstandingIdCollection = collect($outstandingIds ?? []); - @endphp + + + @php + $outstandingIdCollection = collect($outstandingIds ?? []); + + // ========================================== + // FILTER PERIODE BERJALAN KHUSUS UNTUK MINI CARDS + // (Agar Angka sinkron dengan tabel filter JS di bawah) + // ========================================== + $startDayIndex = (int) env('STARTING_DATE', 11); + $closingDayIndex = (int) env('CLOSING_DATE', 13); + $nowIndex = \Carbon\Carbon::now(); + + if ($nowIndex->day >= $startDayIndex) { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->addMonth()->year, $nowIndex->copy()->addMonth()->month, $closingDayIndex)->endOfDay(); + } else { + $startPeriodIndex = \Carbon\Carbon::create($nowIndex->copy()->subMonth()->year, $nowIndex->copy()->subMonth()->month, $startDayIndex)->startOfDay(); + $endPeriodIndex = \Carbon\Carbon::create($nowIndex->year, $nowIndex->month, $closingDayIndex)->endOfDay(); + } + + // Penampung data pengajuan MURNI HANYA DI PERIODE BERJALAN SAAT INI + $currentPeriodForms = $forms->filter(function($form) use ($startPeriodIndex, $endPeriodIndex) { + $tanggalForm = \Carbon\Carbon::parse($form->tanggal); + return $tanggalForm->between($startPeriodIndex, $endPeriodIndex); + }); + @endphp +
@@ -25,684 +81,556 @@
-
-
-
- -
-
-
-

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

-

Total Expense

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'), 0, ',', '.') }}

-

Approved Expenses

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

-

Pending Expenses

-
-
- -
-
-
- -
-
-
-

{{ $outstandingCount }}

-

Outstanding Expenses

-

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

-

Klik untuk menampilkan detail

-
-
- -
-
-
- @if($availableBudget > 0) -
-
-
-

Rp {{ number_format($availableBudget, 0, ',', '.') }}

-

Available Budget

-
-
- -
-
-
- @endif -
-
-
+
-
-
-

List Data Forms Vehicle Running Cost Anda

-

- @if (auth()->user()->can('forms.vehicle.create')) - - Add New Expense Vehicle Running Cost - - @endif -

-
-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if (auth()->user()->can('final_approval.view')) - - @endif - - - - - @php - use App\Helpers\NextCloudHelper; - $role = auth()->user()->getRoleNames()[0]; - @endphp - @foreach ($forms as $item) - - - - - - - - - - - - - - - - - - @if (auth()->user()->can('approval.view')) - - @endif - @if (auth()->user()->can('approval2.view')) - - @endif - @if(auth()->user()->can('final_approval.view')) - - @endif - - - @endforeach - -
#No ExpenseNamaCabangTanggalTypeLiterKeteranganTotalTotal ApprovedKm (Odometer)Tipe BensinNomor PolisiBuktiAccount NumberStatusApproval 1Approval 2Final ApprovalAksi
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->cabang->cabang->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('dddd, D MMMM Y HH:mm:ss') }} - {{-- gunakan badge --}} - @if ($item->type == 'gasoline') - Gasoline - @elseif ($item->type == 'parking_toll') - Parking / Toll - @else - Unknown - @endif - {{ $item->liter }}{{ $item->keterangan }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }}{{ $item->jarak }} km{{ $item->tipe_bensin }}{{ $item->nopol }} - @if ($item->bukti_total) - - Download - - @else - - - @endif - {{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif - - {{-- approve / reject button --}} - @if (auth()->user()->can('approval.approve')) - @if ($item->status == 'On Progress') -
- -
- - - @endif - @endif - @if ($item->approved_at) - {{ \Carbon\Carbon::parse($item->approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if (auth()->user()->can('approval2.approve')) - @if ($item->status == 'Approved 1' && $item->approved_at) -
- -
+
+
+
+
+

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

+

Total Expense

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed', 'Final Approved'])->sum('approved_total'), 0, ',', '.') }}

+

Approved Expenses

+
+
+
+
+
+
+
+

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

+

Pending Expenses

+
+
+
+
+
+
+
+

{{ $outstandingCount }}

+

Outstanding Expenses

+

Total Rp {{ number_format($outstandingTotal, 0, ',', '.') }}

+

Klik untuk menampilkan detail

+
+
+
+
+ @if($availableBudget > 0) +
+
+
+

Rp {{ number_format($availableBudget, 0, ',', '.') }}

+

Available Budget

+
+
+
+
+ @endif +
+ + - - @endif - @endif - @if ($item->approved2_at) - {{ \Carbon\Carbon::parse($item->approved2_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- {{-- approve / reject button --}} - @if(auth()->user()->can('final_approval.approve')) - @if ($item->status == 'Approved 2' && $item->approved2_at && !$item->final_approved_at) -
- -
- @else - @if ($item->final_approved_at && auth()->user()->can('final_approval.open')) -
- @csrf - @method('PUT') - -
- @endif - @endif - @endif - @if ($item->final_approved_at) - {{ \Carbon\Carbon::parse($item->final_approved_at)->locale('id')->isoFormat('D MMMM Y HH:mm') }} - @endif -
- @if (auth()->user()->can('forms.vehicle.view')) - - - - @endif - - @php - $canEdit = false; - if (auth()->user()->can('forms.vehicle.edit')) { - if (in_array($role, ['Medical Representatif', 'Area Manager Cabang'])) { - $canEdit = $item->status == 'On Progress'; - } else { - $canEdit = true; - } - } - @endphp - @if ($canEdit) - - - - @endif - - @if (($role != 'Medical Representatif' && auth()->user()->can('forms.vehicle.delete')) || ($role == 'Medical Representatif' && $item->status == 'On Progress')) -
- @csrf - @method('DELETE') - -
- @endif -
-
-
+
+
+
+
+
+
+

Semua

{{ $currentPeriodForms->count() }}
+ +
+
+
+
+
+
+

Progress

{{ $currentPeriodForms->where('status', 'On Progress')->count() }}
+ +
+
+
+
+
+
+

App 1

{{ $currentPeriodForms->where('status', 'Approved 1')->count() }}
+ +
+
+
+
+
+
+

App 2

{{ $currentPeriodForms->where('status', 'Approved 2')->count() }}
+ +
+
+
+
+
+
+

Final

{{ $currentPeriodForms->where('status', 'Final Approved')->count() }}
+ +
+
+
+
+
+
+

Reject

{{ $currentPeriodForms->where('status', 'Rejected')->count() }}
+ +
+
+
+
+
+
+

Closed

{{ $currentPeriodForms->where('status', 'Closed')->count() }}
+ +
+
-
- -
-
- -
-
- -
+ @endsection @section('scripts') - -@endsection + $('#finalApproveModal').on('hidden.bs.modal', function () { + $('.custom-control-input').prop('checked', false); $('#total').val('0'); + }); + }); + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/report/all.blade.php b/resources/views/backend/pages/report/all.blade.php index 8bf5f2a..04f2ddd 100644 --- a/resources/views/backend/pages/report/all.blade.php +++ b/resources/views/backend/pages/report/all.blade.php @@ -5,288 +5,421 @@ @endsection @section('admin-content') -
-
-
+
+
+
-

{{ $pageInfo['title'] }}

+

{{ $pageInfo['title'] }}

-
- -
-
-
-

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

-

Total Expense

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Closed'])->sum('approved_total'), 0, ',', '.') }}

-

Approved Expenses

-
-
- -
-
-
- -
-
-
-

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

-

Pending Expenses

-
-
- -
-
-
- -
-
-
- {{--

Rp {{ number_format($availableBudget, 0, ',', '.') }}

--}} - {{-- check if availableBudget is not - --}} -

{{ $availableBudget == '0' ? '-' : 'Rp' . number_format($availableBudget, 0, ',', '.') }}

-

Available Budget

-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
+ {{-- Summary Widgets --}} +
+
+
+
+

Rp {{ number_format($forms->sum('total'), 0, ',', '.') }}

+
Total Submission
+
+
+
+
+
+
+
+

Rp {{ number_format($forms->whereIn('status', ['Approved', 'Approved 1', 'Approved 2', 'Closed', 'Final Approved'])->sum('approved_total'), 0, ',', '.') }}

+
Total Approved
+
+
+
+
+
+
+
+

Rp {{ number_format($forms->where('status', 'On Progress')->sum('total'), 0, ',', '.') }}

+
Total Pending
+
+
+
+
+
+
+
+

{{ !$availableBudget || $availableBudget == 0 ? '-' : 'Rp ' . number_format((float)$availableBudget, 0, ',', '.') }}

+
Budget Available
+
+
+
+
+
-
-
- - -
-
- -
- -
-
- - Reset -
-
-
-
-
-
-
+ {{-- Filter Section - Sistem Periode Bulan Akuntansi --}} +
+
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + Reset +
+
+
+
+
+
+
- @php - use App\Helpers\NextCloudHelper; - @endphp + @php use App\Helpers\NextCloudHelper; @endphp
-
-
+
-

All Forms

-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - @foreach ($forms as $item) - - - - - - - - - - - - - @endforeach - -
#No ExpenseTipeNamaRegionCabangTotalTotal ApprovedAccount NumberStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->type }}{{ $item->user->name }}{{ $item->user->region->cabang->region->name }}{{ $item->user->cabang->cabang->name }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }}{{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif -
-
+

Database Konsolidasi (All Forms)

+
+ @include('backend.layouts.partials.messages') + + + + + + + + + + + + + + + + + @foreach ($forms as $item) + + + + + + + + + + + + + + {{-- Kolom Detail Rejeksi Sinkron --}} + + + @endforeach + +
#No ExpenseKategoriNama PengajuRegion / CabangTgl. DibuatNominal (Appr)BuktiStatusDetail Rejeksi
{{ $loop->iteration }}{{ $item->expense_number }} + + {{ str_replace(['form_', '_'], ['', ' '], (string)$item->type) }} + + {{ $item->user->name ?? '-' }} + {{ $item->user->region->cabang->region->name ?? '-' }} + {{ $item->user->cabang->cabang->name ?? '-' }} + + @php $createdAt = \Carbon\Carbon::parse($item->created_at); @endphp +
{{ $createdAt->locale('id')->translatedFormat('d F Y') }}
+ {{ $createdAt->format('H:i') }} WIB +
+ {{ $item->approved_total ? 'Rp ' . number_format($item->approved_total, 0, ',', '.') : '-' }} + + @php + $fileData = []; + $imgExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']; + if (!empty($item->main_proof)) { + $ext = strtolower(pathinfo($item->main_proof, PATHINFO_EXTENSION)); + $fileData[] = [ + 'url' => NextCloudHelper::getFileUrl($item->main_proof), + 'is_img' => in_array($ext, $imgExts) + ]; + } + if(isset($item->extra_attachments)) { + foreach ($item->extra_attachments as $att) { + $ext = strtolower(pathinfo($att->file_path, PATHINFO_EXTENSION)); + $fileData[] = [ + 'url' => NextCloudHelper::getFileUrl($att->file_path), + 'is_img' => in_array($ext, $imgExts) + ]; + } + } + $uniqueFiles = collect($fileData)->unique('url')->values()->all(); + @endphp + + @if (count($uniqueFiles) > 0) + + @else + - + @endif + + @php + $badgeClass = match($item->status) { + 'On Progress' => 'badge-warning text-white', + 'Approved 1', 'Approved 2', 'Closed', 'Final Approved' => 'badge-success text-white', + 'Rejected' => 'badge-danger text-white', + default => 'badge-secondary' + }; + @endphp + {{ $item->status }} + + {{-- Tgl Final Approved --}} + @if(isset($item->report_date) && in_array($item->status, ['Approved 2', 'Closed', 'Final Approved'])) +
+ + Final Approved: + + + {{ \Carbon\Carbon::parse($item->report_date)->translatedFormat('d M Y') }} + +
+ @endif + + {{-- Penanda Tgl Waktu Rejeksi --}} + @if($item->status == 'Rejected' && isset($item->rejected_at)) +
+ + Rejected At: + + + {{ \Carbon\Carbon::parse($item->rejected_at)->translatedFormat('d M Y H:i') }} WIB + +
+ @endif +
+ @if($item->status == 'Rejected') +
+
+ Oleh: + {{ $item->rejector->name ?? $item->rejected_by ?? 'System' }} +
+
+ Alasan: + "{{ $item->remarks ?? '-' }}" +
+
+ @else + - + @endif +
-
+
+ + {{-- MODAL PREVIEW --}} + + + {{-- MODAL ZOOM --}} + @endsection @section('scripts') + $(document).ready(function() { + if ($('#dataTable-table').length) { + $('#dataTable-table').DataTable({ + responsive: false, + dom: 'Blfrtip', + deferRender: true, // Optimasi performa loading render DOM fisik tabel + lengthMenu: [ [10, 25, 50, -1], [10, 25, 50, "All"] ], + buttons: [ + { + extend: 'excelHtml5', + text: ' Excel', + className: 'btn-success', + exportOptions: { + columns: [0, 1, 2, 3, 4, 5, 6, 8, 9], + format: { + body: function (data, row, column, node) { + let text = node.textContent || node.innerText || ""; + text = text.replace(/\s+/g, ' ').trim(); + + if (column === 6) { + return text.replace(/Rp\s?|[.]/g, ''); + } + return text; + } + } + } + }, + { + extend: 'pdfHtml5', + text: ' PDF', + className: 'btn-danger', + orientation: 'landscape', + pageSize: 'A4', + exportOptions: { columns: [0, 1, 2, 3, 4, 5, 6, 8, 9] }, + customize: function(doc) { + doc.defaultStyle.fontSize = 7; + doc.styles.tableHeader.fontSize = 8; + } + }, + 'colvis' + ] + }); + } - -@endsection + if(files && files.length > 0) { + files.forEach((file) => { + html += ` +
+
+
+
+ ${file.is_img + ? `` + : `

Dokumen Digital

` + } +
+
+ ${file.is_img + ? `` + : ` Open PDF` + } + Download +
+
+
+
`; + }); + } + $('#containerPreview').html(html + '
'); + $('#modalPreviewBukti').modal('show'); + }); + + function loadCabang(regionCode, selectedCabang = null) { + const cabangSelect = $('#cabang'); + if (regionCode) { + fetch(`/api/cabang-by-region/${regionCode}`) + .then(response => response.json()) + .then(data => { + cabangSelect.html(''); + data.forEach(cabang => { + const isSelected = (selectedCabang && cabang.code === selectedCabang) ? 'selected' : ''; + cabangSelect.append(``); + }); + }); + } + } + + $('#region').on('change', function() { loadCabang(this.value); }); + const currentRegion = '{{ request()->region }}'; + const currentCabang = '{{ request()->cabang }}'; + if (currentRegion) { loadCabang(currentRegion, currentCabang); } + }); + + function openFullImage(url) { + $('#imgFullDisplay').attr('src', url); + $('#modalImageFull').modal('show'); + } + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/report/entertainment.blade.php b/resources/views/backend/pages/report/entertainment.blade.php index a03c976..ec960f2 100644 --- a/resources/views/backend/pages/report/entertainment.blade.php +++ b/resources/views/backend/pages/report/entertainment.blade.php @@ -9,221 +9,440 @@
-

{{ $pageInfo['title'] }}

+

{{ $pageInfo['title'] }}

-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
- - Reset -
-
-
-
-
-
-
+ + {{-- Filter Section - Sistem Periode Bulan Akuntansi (11 s/d 13 Bulan Berikut) --}} +
+
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + Reset +
+
+
+
+
+
+
- @php - use App\Helpers\NextCloudHelper; - @endphp + @php use App\Helpers\NextCloudHelper; @endphp
-
-
+
-

Form Entertainment & Presentation

-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - - - - - @foreach ($forms as $item) - - - - - - - - - - - - - - - - - @endforeach - -
#No ExpenseUserRegionCabangNama PenerimaTanggalJenisKeteranganTotalTotal ApprovedBuktiAccount NumberStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->region->cabang->region->name }}{{ $item->user->cabang->cabang->name }}{{ $item->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('D MMMM Y') }}{{ $item->jenis }}{{ $item->keterangan }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }} - @if ($item->bukti_total) - - Download - - @else - - - @endif - {{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif -
-
+

List Report Entertainment & Presentation

+
+ + + + + + + + + + + + + + + + + + + + + @foreach ($forms as $item) + + + + + + + + + + + + + + + + + + + + + + + @endforeach + +
#No ExpenseAccount No.Pengaju / PenerimaNIK/NPWP & AlamatNama PerusahaanJabatanJenis UsahaRegion / CabangTgl. Digunakan & DibuatJenis & KeteranganTotal ApprovedBuktiStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->account_number ?? ($item->kategori->account_number ?? '-') }} +
{{ $item->user->name ?? '-' }}
+ Penerima: {{ $item->name }} +
+
{{ $item->nik_or_npwp ?? '-' }}
+
+ {{ $item->alamat ?? '-' }} +
+
{{ $item->nama_perusahaan ?? '-' }}{{ $item->jabatan ?? '-' }}{{ $item->jenis_usaha ?? '-' }} + {{ $item->user->region->cabang->region->name ?? '-' }} + {{ $item->user->cabang->cabang->name ?? '-' }} + +
+ {{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} +
+
+ {{ $item->created_at->format('d M Y') }} {{ $item->created_at->format('H:i') }} +
+
+ {{ $item->jenis }} +
{{ $item->keterangan }}
+
+ {{ $item->approved_total ? number_format($item->approved_total, 0, ',', '.') : '-' }} + + @php + $fileData = []; + $imgExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']; + + if (!empty($item->bukti_total)) { + $extension = strtolower(pathinfo($item->bukti_total, PATHINFO_EXTENSION)); + $fileData[] = [ + 'url' => NextCloudHelper::getFileUrl($item->bukti_total), + 'is_img' => in_array($extension, $imgExts) + ]; + } + + if ($item->attachments) { + foreach ($item->attachments as $att) { + if ($att->file_path) { + $extension = strtolower(pathinfo($att->file_path, PATHINFO_EXTENSION)); + $fileData[] = [ + 'url' => NextCloudHelper::getFileUrl($att->file_path), + 'is_img' => in_array($extension, $imgExts) + ]; + } + } + } + $uniqueFiles = collect($fileData)->unique('url')->values()->all(); + @endphp + + @if (count($uniqueFiles) > 0) + + @else + - + @endif + + @php + $badgeClass = match($item->status) { + 'On Progress' => 'badge-warning text-white', + 'Approved 1', 'Approved 2', 'Closed' => 'badge-success text-white', + 'Rejected' => 'badge-danger text-white', + default => 'badge-secondary' + }; + @endphp + {{ $item->status }} + + {{-- BLOK AUDIT TRAIL REJECTED --}} + @if($item->status === 'Rejected') +
+ + Detail Penolakan: + + + Oleh: {{ $item->rejector->name ?? $item->rejected_by ?? 'System' }} + + @if($item->rejected_at) + + Waktu: {{ \Carbon\Carbon::parse($item->rejected_at)->translatedFormat('d M Y H:i') }} WIB + + @elseif($item->updated_at && $item->remarks) + + Waktu: {{ \Carbon\Carbon::parse($item->updated_at)->translatedFormat('d M Y H:i') }} WIB + + @endif + @if($item->remarks) + + Ket: "{{ $item->remarks }}" + + @endif +
+ @endif + + {{-- BLOK EXISTING FINAL APPROVED / CLOSED --}} + @if($item->final_approved_at && in_array($item->status, ['Approved 2', 'Closed'])) +
+ + Final Approved: + + + {{ \Carbon\Carbon::parse($item->final_approved_at)->translatedFormat('d M Y') }} + {{ \Carbon\Carbon::parse($item->final_approved_at)->format('H:i') }} WIB + +
+ @endif +
-
+
+ + {{-- MODAL 1: DAFTAR LAMPIRAN --}} + + + {{-- MODAL 2: ZOOM GAMBAR FULL --}} + @endsection @section('scripts') + $(document).ready(function() { + if ($('#dataTable-table').length) { + $('#dataTable-table').DataTable({ + responsive: false, + dom: 'Blfrtip', + deferRender: true, // Membuat pemuatan ribuan baris menjadi instan & ringan + lengthMenu: [ [10, 50, 100, -1], [10, 50, 100, "All"] ], + buttons: [ + { + extend: 'excelHtml5', + text: ' Excel', + className: 'btn-success', + exportOptions: { + columns: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13], + format: { + body: function (data, row, column, node) { + let text = node.textContent || node.innerText || ""; + text = text.replace(/\s+/g, ' ').trim(); - -@endsection + var tableObj; + for (var i = 0; i < doc.content.length; i++) { + if (doc.content[i].table !== undefined) { + tableObj = doc.content[i]; + break; + } + } + + if(tableObj) { + tableObj.table.widths = [ + '2%', '11%', '6%', '8%', '9%', '8%', '7%', '7%', '8%', '9%', '11%', '8%', '6%' + ]; + tableObj.layout = { + hLineWidth: function(i, node) { return 0.5; }, + vLineWidth: function(i, node) { return 0.5; }, + hLineColor: function(i, node) { return '#aaa'; }, + vLineColor: function(i, node) { return '#aaa'; }, + paddingLeft: function(i, node) { return 3; }, + paddingRight: function(i, node) { return 3; }, + paddingTop: function(i, node) { return 3; }, + paddingBottom: function(i, node) { return 3; } + }; + } + } + }, + 'colvis' + ] + }); + } + + $(document).on('click', '.btn-preview-bukti', function(e) { + e.preventDefault(); + const expenseNum = $(this).data('expense'); + const files = $(this).data('files'); + let html = '
'; + + $('#textExpenseNum').text('#' + expenseNum); + $('#containerPreview').html('
'); + + if(files && files.length > 0) { + files.forEach((file) => { + html += ` +
+
+
+
+ ${file.is_img + ? `` + : `

Dokumen Digital

` + } +
+
+ ${file.is_img + ? `` + : ` Buka PDF` + } + Download +
+
+
+
`; + }); + } + $('#containerPreview').html(html + '
'); + $('#modalPreviewBukti').modal('show'); + }); + + function loadCabang(regionCode, selectedCabang = null) { + const cabangSelect = $('#cabang'); + if (regionCode) { + fetch(`/api/cabang-by-region/${regionCode}`) + .then(response => response.json()) + .then(data => { + cabangSelect.html(''); + data.forEach(cabang => { + const isSelected = (selectedCabang && cabang.code === selectedCabang) ? 'selected' : ''; + cabangSelect.append(``); + }); + }); + } + } + $('#region').on('change', function() { loadCabang(this.value); }); + const currentRegion = '{{ request()->region }}'; + const currentCabang = '{{ request()->cabang }}'; + if (currentRegion) { loadCabang(currentRegion, currentCabang); } + }); + + function openFullImage(url) { + $('#imgFullDisplay').attr('src', url); + $('#modalImageFull').modal('show'); + } + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/report/generate_jurnal.blade.php b/resources/views/backend/pages/report/generate_jurnal.blade.php index b34c1a8..bd8c05c 100644 --- a/resources/views/backend/pages/report/generate_jurnal.blade.php +++ b/resources/views/backend/pages/report/generate_jurnal.blade.php @@ -5,272 +5,272 @@ @endsection @section('admin-content') -
-
-
+
+
+
-

{{ $pageInfo['title'] }}

+

{{ $pageInfo['title'] }}

-
-
-
-
-
    -
  • Cabang: {{ $cabang->name }}
  • -
  • Periode: {{ ucfirst($month) }} {{ $year }}
  • -
  • Cost Centre {{ $cabang->cost->code }}
  • -
-
-
-
-
-
-
- @php - use App\Helpers\FormHelper; - @endphp +
+
+
+
+
+
+
    +
  • Cabang: {{ $cabang->name }}
  • +
  • + Periode: {{ ucfirst($month) }} {{ $year }} + Periode Accounting: {{ request()->accounting_period ?? '-' }} +
  • +
  • Cost Centre: {{ $cabang->cost->code }}
  • +
+
+
+
+
+
+
+
+
+ + @php + /** + * PEMBARUAN LOGIKA: + * 1. Building Maintenance Dihapus + * 2. Presentation Ditambahkan di urutan ke-7 + * 3. Account Number disesuaikan dengan image_01e273.png + */ + $configJurnal = [ + ['name' => 'Telp./ Fax / Email', 'account' => '800 104 00'], + ['name' => 'Listrik', 'account' => '800 105 00'], + ['name' => 'Postages / Courier', 'account' => '800 107 00'], + ['name' => 'Office Supplies / Bank Changes / Interest', 'account' => '800 108 00'], + ['name' => 'Relokasi', 'account' => '800 111 00'], + ['name' => 'Meeting / Seminar', 'account' => '800 201 00'], + ['name' => 'Presentation', 'account' => '800 304 00'], // Urutan ke-7 + ['name' => 'Up Country', 'account' => '800 305 00'], + ['name' => 'Vehicle Running Cost', 'account' => '800 306 00'], + ['name' => 'Field Force', 'account' => '800 308 00'], + ['name' => 'Entertainment', 'account' => '800 309 00'], + ['name' => 'Rumah Tangga', 'account' => '800 213 00'], + ]; + + $totalDr = 0; + $totalCr = 0; + $counter = 1; + + // Mencari ID Kategori berdasarkan nama untuk mengambil nilai dari array $nominals + $kategoriCollection = collect($kategori); + @endphp
-
- -
-
-
-
-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - @php - $totalDr = 0; - $totalCr = 0; - @endphp - @foreach ($kategori as $item) - @php - $drValue = $nominals[$item->id] ?? 0; - $drFormatted = $drValue == 0 ? '' : number_format($drValue, 0, ',', ','); - $totalDr += $drValue; - @endphp +
+
+
+
+
+
+ @include('backend.layouts.partials.messages') +
#TypeAccount NumberDrCr
+ + + + + + + + + + + {{-- 1 - 12: Loop berdasarkan config yang sudah diurutkan --}} + @foreach ($configJurnal as $conf) + @php + // Ambil objek kategori dari database yang namanya cocok + $katObj = $kategoriCollection->where('name', $conf['name'])->first(); + $drValue = $katObj ? ($nominals[$katObj->id] ?? 0) : 0; + + $totalDr += $drValue; + @endphp + + + + + + + + @endforeach - - - - - - - - + {{-- 13: Cash in Bank BCA --}} + @php + $cashInBankValue = $pettyCashAmount - $totalKategori; + @endphp + + + + + @if ($cashInBankValue >= 0) + @php $totalDr += $cashInBankValue; @endphp + + + @else + @php $totalCr += abs($cashInBankValue); @endphp + + + @endif + - - @endforeach + {{-- 14: Petty Cash --}} + + + + + + + - - - @php - $cashInBankValue = $pettyCashAmount - $totalKategori; - $rowIndex = $kategori->count() + 1; - @endphp - - - - - @if ($cashInBankValue >= 0) - @php - $totalDr += $cashInBankValue; - @endphp - - - @else - @php - $totalCr += abs($cashInBankValue); - @endphp - - - @endif - - + {{-- 15: Cash Advance (Opening) --}} + + + + + + + - - - - - - - - - - {{-- Static Rows --}} - - - - - - - - - - - - - - - - - - - - - - - -
#TypeAccount NumberDrCr
{{ $counter++ }}{{ $conf['name'] }}{{ $conf['account'] }}{{ $drValue == 0 ? '' : number_format($drValue, 0, ',', '.') }}
{{ $loop->iteration }}{{ $item->name }}{{ $item->account_number }}{{ $drFormatted }}
{{ $counter++ }}Cash in Bank BCA170 600 00{{ $cashInBankValue == 0 ? '' : number_format($cashInBankValue, 0, ',', '.') }}{{ number_format(abs($cashInBankValue), 0, ',', '.') }}
{{ $counter++ }}Petty Cash + @php $totalCr += $pettyCashAmount; @endphp + {{ $pettyCashAmount == 0 ? '' : number_format($pettyCashAmount, 0, ',', '.') }} +
{{ $rowIndex }}Cash in Bank BCA{{ $kategori_bca->account_number }}{{ $cashInBankValue == 0 ? '' : number_format($cashInBankValue, 0, ',', ',') }}{{ number_format(abs($cashInBankValue), 0, ',', ',') }}
{{ $counter++ }}Cash Advance (Opening Balance)170 600 00 + @php $totalCr += 500000; @endphp + {{ number_format(500000, 0, ',', '.') }} +
{{ $rowIndex + 1 }}Petty Cash - @php - $totalCr += $pettyCashAmount; - echo $pettyCashAmount == 0 ? '' : number_format($pettyCashAmount, 0, ',', ','); - @endphp -
{{ $rowIndex + 2 }}Cash Advance (Opening Balance){{ $kategori_bca->account_number }} - @php - $totalCr += 500000; - @endphp - {{ number_format(500000, 0, ',', ',') }} -
{{ $rowIndex + 3 }}Cash Advance (Ending Balance){{ $kategori_bca->account_number }} - @php - $totalDr += 500000; - @endphp - {{ number_format(500000, 0, ',', ',') }} -
Total{{ number_format($totalDr, 0, ',', '.') }}{{ number_format($totalCr, 0, ',', '.') }}
-
-
-
-
-
-
-
-
+ {{-- 16: Cash Advance (Ending) --}} + + {{ $counter++ }} + Cash Advance (Ending Balance) + 170 600 00 + + @php $totalDr += 500000; @endphp + {{ number_format(500000, 0, ',', '.') }} + + + + + + + Total + {{ number_format($totalDr, 0, ',', '.') }} + {{ number_format($totalCr, 0, ',', '.') }} + + + +
+
+
+
+
+ +
@endsection @section('scripts') -@endsection +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/report/jurnal.blade.php b/resources/views/backend/pages/report/jurnal.blade.php index d8a6cf0..24543c3 100644 --- a/resources/views/backend/pages/report/jurnal.blade.php +++ b/resources/views/backend/pages/report/jurnal.blade.php @@ -7,123 +7,147 @@ @section('admin-content')
-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
+
+
+

{{ $pageInfo['title'] }}

+
+
+ +
+
-
-
- - -
-
- -
+ {{-- Filter Filter Generator Jurnal - Sinkron 11 s/d 13 Bulan Berikut --}} +
+
+
+
+ +
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
-
-
- - Reset -
-
- -
-
-
-
+
+
+ + +
+
+ +
+
+ + +
+
+
+ +
+
+
+ + Reset +
+
+
+ +
+
+
+
@endsection @section('scripts') - + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/report/meeting.blade.php b/resources/views/backend/pages/report/meeting.blade.php index b343345..9f6b96c 100644 --- a/resources/views/backend/pages/report/meeting.blade.php +++ b/resources/views/backend/pages/report/meeting.blade.php @@ -9,211 +9,354 @@
-

{{ $pageInfo['title'] }}

+

{{ $pageInfo['title'] }}

-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
- - Reset -
-
-
-
-
-
-
+ + {{-- Filter Section - Disinkronkan dengan Sistem Periode Bulan Akuntansi (11 s/d 13 Bulan Berikut) --}} +
+
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + Reset +
+
+
+
+
+
+
- @php - use App\Helpers\NextCloudHelper; - @endphp + @php use App\Helpers\NextCloudHelper; @endphp
-
-
+
-

Form Meeting & Seminar

-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - - - - @foreach ($forms as $item) - - - - - - - - - - - - - - - - @endforeach - -
#No ExpenseNamaRegionCabangTanggalAllowanceTransport AnkotHotelTotalTotal ApprovedAccount NumberStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->region->cabang->region->name }}{{ $item->user->cabang->cabang->name }}{{ \Carbon\Carbon::parse($item->created_at)->locale('id')->isoFormat('D MMMM Y') }}{{ number_format($item->allowance, 0, ',', '.') }}{{ number_format($item->transport_ankot, 0, ',', '.') }}{{ number_format($item->hotel, 0, ',', '.') }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }}{{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif -
-
+

List Report Meeting & Seminar

+
+ + + + + + + + + + + + + + + + @foreach ($forms as $item) + + + + + + + + + + {{-- LOGIKA ATTACHMENT MULTI-KOLOM --}} + + + + + @endforeach + +
#No ExpensePengajuRegion / CabangTgl. PenggunaanTgl. DibuatTotal ApprovedBuktiStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name ?? '-' }} + {{ $item->user->region->cabang->region->name ?? '-' }} + {{ $item->user->cabang->cabang->name ?? '-' }} + {{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} +
{{ $item->created_at->format('d M Y') }}
+ {{ $item->created_at->format('H:i') }} WIB +
{{ $item->approved_total ? number_format($item->approved_total, 0, ',', '.') : '-' }} + @php + $fileData = []; + $imgExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']; + $rawColumns = ['bukti_allowance', 'bukti_transport_ankot', 'bukti_hotel']; + + foreach ($rawColumns as $col) { + if (!empty($item->$col)) { + $ext = strtolower(pathinfo($item->$col, PATHINFO_EXTENSION)); + $fileData[] = ['url' => NextCloudHelper::getFileUrl($item->$col), 'is_img' => in_array($ext, $imgExts)]; + } + } + + if ($item->attachments) { + foreach ($item->attachments as $att) { + if ($att->file_path) { + $ext = strtolower(pathinfo($att->file_path, PATHINFO_EXTENSION)); + $fileData[] = ['url' => NextCloudHelper::getFileUrl($att->file_path), 'is_img' => in_array($ext, $imgExts)]; + } + } + } + $uniqueFiles = collect($fileData)->unique('url')->values()->all(); + @endphp + + @if (count($uniqueFiles) > 0) + + @else + - + @endif + + @php + $badgeClass = match($item->status) { + 'On Progress' => 'badge-warning text-white', + 'Approved 1', 'Approved 2', 'Closed' => 'badge-success text-white', + 'Rejected' => 'badge-danger text-white', + default => 'badge-secondary' + }; + @endphp + {{ $item->status }} + + {{-- BLOK AUDIT TRAIL LOG REJECTED --}} + @if($item->status === 'Rejected') +
+ + Detail Penolakan: + + + Oleh: {{ $item->rejector->name ?? $item->rejected_by ?? 'System' }} + + @if($item->rejected_at) + + Waktu: {{ \Carbon\Carbon::parse($item->rejected_at)->translatedFormat('d M Y H:i') }} WIB + + @elseif($item->updated_at && $item->remarks) + + Waktu: {{ \Carbon\Carbon::parse($item->updated_at)->translatedFormat('d M Y H:i') }} WIB + + @endif + @if($item->remarks) + + Ket: "{{ $item->remarks }}" + + @endif +
+ @endif + + {{-- BLOK EXISTING FINAL APPROVED / CLOSED --}} + @if($item->final_approved_at && in_array($item->status, ['Approved 2', 'Closed'])) +
+ + Final Approved: + + + {{ \Carbon\Carbon::parse($item->final_approved_at)->translatedFormat('d M Y') }} + {{ \Carbon\Carbon::parse($item->final_approved_at)->format('H:i') }} WIB + +
+ @endif +
-
+
+ + {{-- MODAL PREVIEW & ZOOM --}} + + + @endsection @section('scripts') - + -@endsection + if(files && files.length > 0) { + files.forEach((file) => { + html += ` +
+
+
+
+ ${file.is_img + ? `` + : `

Dokumen Digital

` + } +
+
+ ${file.is_img + ? `` + : ` Buka PDF` + } + Download +
+
+
+
`; + }); + } + $('#containerPreview').html(html + ''); + $('#modalPreviewBukti').modal('show'); + }); + + function loadCabang(regionCode, selectedCabang = null) { + const cabangSelect = $('#cabang'); + if (regionCode) { + fetch(`/api/cabang-by-region/${regionCode}`) + .then(response => response.json()) + .then(data => { + cabangSelect.html(''); + data.forEach(cabang => { + const isSelected = (selectedCabang && cabang.code === selectedCabang) ? 'selected' : ''; + cabangSelect.append(``); + }); + }); + } + } + $('#region').on('change', function() { loadCabang(this.value); }); + const currentRegion = '{{ request()->region }}'; + const currentCabang = '{{ request()->cabang }}'; + if (currentRegion) { loadCabang(currentRegion, currentCabang); } + }); + + function openFullImage(url) { + $('#imgFullDisplay').attr('src', url); + $('#modalImageFull').modal('show'); + } + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/report/others.blade.php b/resources/views/backend/pages/report/others.blade.php index 1f4e503..62152bd 100644 --- a/resources/views/backend/pages/report/others.blade.php +++ b/resources/views/backend/pages/report/others.blade.php @@ -9,219 +9,357 @@
-

{{ $pageInfo['title'] }}

+

{{ $pageInfo['title'] }}

-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
- - Reset -
-
-
-
-
-
-
+ + {{-- Filter Section - Disinkronkan dengan Sistem Periode Bulan Akuntansi (11 s/d 13 Bulan Berikut) --}} +
+
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + Reset +
+
+
+
+
+
+
- @php - use App\Helpers\NextCloudHelper; - @endphp + @php use App\Helpers\NextCloudHelper; @endphp
-
-
+
-

Form Others

-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - - - - @foreach ($forms as $item) - - - - - - - - - - - - - - - - @endforeach - -
#No ExpenseNamaRegionCabangTanggalJenisKeteranganTotalTotal AprovedBukti TotalAccount NumberStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->region->cabang->region->name }}{{ $item->user->cabang->cabang->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('D MMMM Y') }}{{ $item->kategori->name }}{{ $item->keterangan }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }} - @if ($item->bukti_total) - - Download - - @else - - - @endif - {{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif -
-
+

List Report Form Others

+
+ + + + + + + + + + + + + + + + + @foreach ($forms as $item) + + + + + + + + + + + + + + + @endforeach + +
#No ExpensePengajuRegion / CabangKategori / Ket.Tgl. PenggunaanTgl. DibuatTotal ApprovedBuktiStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name ?? '-' }} + {{ $item->user->region->cabang->region->name ?? '-' }} + {{ $item->user->cabang->cabang->name ?? '-' }} + +
{{ $item->kategori->name ?? '-' }}
+
+ {{ $item->keterangan }} +
+
{{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} +
{{ $item->created_at->format('d M Y') }}
+ {{ $item->created_at->format('H:i') }} WIB +
{{ $item->approved_total ? number_format($item->approved_total, 0, ',', '.') : '-' }} + @php + $fileData = []; + $imgExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']; + + if (!empty($item->bukti_total)) { + $ext = strtolower(pathinfo($item->bukti_total, PATHINFO_EXTENSION)); + $fileData[] = ['url' => NextCloudHelper::getFileUrl($item->bukti_total), 'is_img' => in_array($ext, $imgExts)]; + } + + if ($item->attachments) { + foreach ($item->attachments as $att) { + if ($att->file_path) { + $ext = strtolower(pathinfo($att->file_path, PATHINFO_EXTENSION)); + $fileData[] = ['url' => NextCloudHelper::getFileUrl($att->file_path), 'is_img' => in_array($ext, $imgExts)]; + } + } + } + $uniqueFiles = collect($fileData)->unique('url')->values()->all(); + @endphp + + @if (count($uniqueFiles) > 0) + + @else + - + @endif + + @php + $badgeClass = match($item->status) { + 'On Progress' => 'badge-warning text-white', + 'Approved 1', 'Approved 2', 'Closed' => 'badge-success text-white', + 'Rejected' => 'badge-danger text-white', + default => 'badge-secondary' + }; + @endphp + {{ $item->status }} + + {{-- BLOK AUDIT TRAIL LOG REJECTED --}} + @if($item->status === 'Rejected') +
+ + Detail Penolakan: + + + Oleh: {{ $item->rejector->name ?? $item->rejected_by ?? 'System' }} + + @if($item->rejected_at) + + Waktu: {{ \Carbon\Carbon::parse($item->rejected_at)->translatedFormat('d M Y H:i') }} WIB + + @elseif($item->updated_at && $item->remarks) + + Waktu: {{ \Carbon\Carbon::parse($item->updated_at)->translatedFormat('d M Y H:i') }} WIB + + @endif + @if($item->remarks) + + Ket: "{{ $item->remarks }}" + + @endif +
+ @endif + + {{-- BLOK EXISTING FINAL APPROVED / CLOSED --}} + @if($item->final_approved_at && in_array($item->status, ['Approved 2', 'Closed'])) +
+ + Final Approved: + + + {{ \Carbon\Carbon::parse($item->final_approved_at)->translatedFormat('d M Y') }} + {{ \Carbon\Carbon::parse($item->final_approved_at)->format('H:i') }} WIB + +
+ @endif +
-
+
+ + {{-- MODAL PREVIEW & ZOOM --}} + + + @endsection @section('scripts') - + -@endsection + if(files && files.length > 0) { + files.forEach((file) => { + html += ` +
+
+
+
+ ${file.is_img + ? `` + : `

Dokumen Digital

` + } +
+
+ ${file.is_img + ? `` + : ` Buka PDF` + } + Download +
+
+
+
`; + }); + } + $('#containerPreview').html(html + ''); + $('#modalPreviewBukti').modal('show'); + }); + + function loadCabang(regionCode, selectedCabang = null) { + const cabangSelect = $('#cabang'); + if (regionCode) { + fetch(`/api/cabang-by-region/${regionCode}`) + .then(response => response.json()) + .then(data => { + cabangSelect.html(''); + data.forEach(cabang => { + const isSelected = (selectedCabang && cabang.code === selectedCabang) ? 'selected' : ''; + cabangSelect.append(``); + }); + }); + } + } + $('#region').on('change', function() { loadCabang(this.value); }); + const currentRegion = '{{ request()->region }}'; + const currentCabang = '{{ request()->cabang }}'; + if (currentRegion) { loadCabang(currentRegion, currentCabang); } + }); + + function openFullImage(url) { + $('#imgFullDisplay').attr('src', url); + $('#modalImageFull').modal('show'); + } + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/report/upcountry.blade.php b/resources/views/backend/pages/report/upcountry.blade.php index 35d22e0..1c97100 100644 --- a/resources/views/backend/pages/report/upcountry.blade.php +++ b/resources/views/backend/pages/report/upcountry.blade.php @@ -9,216 +9,356 @@
-

{{ $pageInfo['title'] }}

+

{{ $pageInfo['title'] }}

-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
- - Reset -
-
-
-
-
-
-
+ + {{-- Filter Section - Dioptimasi Menggunakan Dropdown Periode Bulan Akuntansi --}} +
+
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + Reset +
+
+
+
+
+
+
- @php - use App\Helpers\NextCloudHelper; - @endphp + @php use App\Helpers\NextCloudHelper; @endphp
-
-
+
-

Form Up Country

-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - - - - @foreach ($forms as $item) - - - - - - - - - - - - - - - - @endforeach - -
#No ExpenseNamaRegionCabangRayonTanggalTujuanJarakTotalTotal ApprovedAccount NumberStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->region->cabang->region->name }}{{ $item->user->cabang->cabang->name }}{{ $item->rayon->code }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('D MMMM Y') }}{{ $item->tujuan }}{{ $item->jarak }} km{{ number_format($item->total, 0, ',', '.') }} - @if ($item->approved_total) - {{ number_format($item->approved_total, 0, ',', '.') }} - @else - - - @endif - {{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif -
-
+

List Report Up Country

+
+ + + + + + + + + + + + + + + + + + @foreach ($forms as $item) + + + + + + + + + + + + + + + + @endforeach + +
#No ExpensePengajuRegion / CabangRayonTgl. PenggunaanTgl. DibuatTujuan / JarakTotal ApprovedBuktiStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name ?? '-' }} + {{ $item->user->region->cabang->region->name ?? '-' }} + {{ $item->user->cabang->cabang->name ?? '-' }} + {{ $item->rayon->code ?? '-' }}{{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} +
{{ $item->created_at->format('d M Y') }}
+ {{ $item->created_at->format('H:i') }} WIB +
+
{{ $item->tujuan }}
+
{{ $item->jarak }} km
+
{{ $item->approved_total ? number_format($item->approved_total, 0, ',', '.') : '-' }} + @php + $fileData = []; + $imgExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']; + + if ($item->attachments) { + foreach ($item->attachments as $att) { + if ($att->file_path) { + $ext = strtolower(pathinfo($att->file_path, PATHINFO_EXTENSION)); + $fileData[] = [ + 'url' => NextCloudHelper::getFileUrl($att->file_path), + 'is_img' => in_array($ext, $imgExts) + ]; + } + } + } + $uniqueFiles = collect($fileData)->unique('url')->values()->all(); + @endphp + + @if (count($uniqueFiles) > 0) + + @else + - + @endif + + @php + $badgeClass = match($item->status) { + 'On Progress' => 'badge-warning text-white', + 'Approved 1', 'Approved 2', 'Closed' => 'badge-success text-white', + 'Rejected' => 'badge-danger text-white', + default => 'badge-secondary' + }; + @endphp + {{ $item->status }} + + {{-- BLOK AUDIT TRAIL REJECTED --}} + @if($item->status === 'Rejected') +
+ + Detail Penolakan: + + + Oleh: {{ $item->rejector->name ?? $item->rejected_by ?? 'System' }} + + @if($item->rejected_at) + + Waktu: {{ \Carbon\Carbon::parse($item->rejected_at)->translatedFormat('d M Y H:i') }} WIB + + @elseif($item->updated_at && $item->remarks) + + Waktu: {{ \Carbon\Carbon::parse($item->updated_at)->translatedFormat('d M Y H:i') }} WIB + + @endif + @if($item->remarks) + + Ket: "{{ $item->remarks }}" + + @endif +
+ @endif + + {{-- BLOK EXISTING FINAL APPROVED / CLOSED --}} + @if($item->final_approved_at && in_array($item->status, ['Approved 2', 'Closed'])) +
+ + Final Approved: + + + {{ \Carbon\Carbon::parse($item->final_approved_at)->translatedFormat('d M Y') }} + {{ \Carbon\Carbon::parse($item->final_approved_at)->format('H:i') }} WIB + +
+ @endif +
-
+
+ + {{-- MODAL PREVIEW --}} + + + {{-- MODAL ZOOM --}} + @endsection @section('scripts') + $(document).ready(function() { + if ($('#dataTable-table').length) { + $('#dataTable-table').DataTable({ + responsive: false, + dom: 'Blfrtip', + deferRender: true, // Optimasi performa DataTables untuk memuat ribuan baris tanpa freeze + lengthMenu: [ [10, 50, 100, -1], [10, 50, 100, "All"] ], + buttons: [ + { + extend: 'excelHtml5', text: ' Excel', className: 'btn-success', + exportOptions: { + columns: [0, 1, 2, 3, 4, 5, 6, 7, 8, 10], + format: { + body: function (data, row, column, node) { + let text = node.textContent || node.innerText || ""; + if (column === 8) return text.replace(/[.]/g, ''); + return text.replace(/\s+/g, ' ').trim(); + } + } + } + }, + { + extend: 'pdfHtml5', text: ' PDF', className: 'btn-danger', + orientation: 'landscape', pageSize: 'A4', + exportOptions: { columns: [0, 1, 2, 3, 4, 5, 6, 7, 8, 10] }, + customize: function(doc) { + doc.defaultStyle.fontSize = 8; + doc.styles.tableHeader.fontSize = 9; + } + }, + 'colvis' + ] + }); + } - -@endsection + if(files && files.length > 0) { + files.forEach((file) => { + html += ` +
+
+
+
+ ${file.is_img + ? `` + : `

Dokumen Digital

` + } +
+
+ ${file.is_img + ? `` + : ` Buka PDF` + } + Download +
+
+
+
`; + }); + } + $('#containerPreview').html(html + ''); + $('#modalPreviewBukti').modal('show'); + }); + + function loadCabang(regionCode, selectedCabang = null) { + const cabangSelect = $('#cabang'); + if (regionCode) { + fetch(`/api/cabang-by-region/${regionCode}`) + .then(response => response.json()) + .then(data => { + cabangSelect.html(''); + data.forEach(cabang => { + const isSelected = (selectedCabang && cabang.code === selectedCabang) ? 'selected' : ''; + cabangSelect.append(``); + }); + }); + } + } + $('#region').on('change', function() { loadCabang(this.value); }); + const currentRegion = '{{ request()->region }}'; + const currentCabang = '{{ request()->cabang }}'; + if (currentRegion) { loadCabang(currentRegion, currentCabang); } + }); + + function openFullImage(url) { + $('#imgFullDisplay').attr('src', url); + $('#modalImageFull').modal('show'); + } + +@endsection \ No newline at end of file diff --git a/resources/views/backend/pages/report/vehicle.blade.php b/resources/views/backend/pages/report/vehicle.blade.php index b1eccfb..ce6c111 100644 --- a/resources/views/backend/pages/report/vehicle.blade.php +++ b/resources/views/backend/pages/report/vehicle.blade.php @@ -9,223 +9,362 @@
-

{{ $pageInfo['title'] }}

+

{{ $pageInfo['title'] }}

-
-
-
-
-
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
-
- - -
-
-
- -
-
- - Reset -
-
-
-
-
-
-
+ + {{-- Filter Section - Diperbarui ke Sistem Periode Bulan Akuntansi --}} +
+
+
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+ + +
+
+
+ + Reset +
+
+
+
+
+
+
- @php - use App\Helpers\NextCloudHelper; - @endphp + @php use App\Helpers\NextCloudHelper; @endphp
-
-
+
-

Form Vehicle Running Cost

-
- @include('backend.layouts.partials.messages') -
- - - - - - - - - - - - - - - - - - - - - - @foreach ($forms as $item) - - - - - - - - - - - - - - - - - - @endforeach - -
#No ExpenseNamaRegionCabangTanggalLiterTotalTotal ApprovedJarakTipe BensinNomor PolisiBuktiAccount NumberStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name }}{{ $item->user->region->cabang->region->name }}{{ $item->user->cabang->cabang->name }}{{ \Carbon\Carbon::parse($item->tanggal)->locale('id')->isoFormat('dddd, D MMMM Y HH:mm:ss') }}{{ $item->liter }}{{ number_format($item->total, 0, ',', '.') }}{{ number_format($item->approved_total, 0, ',', '.') }}{{ $item->jarak }} km{{ $item->tipe_bensin }}{{ $item->nopol }} - @if ($item->bukti_total) - - Download - - @else - - - @endif - {{ $item->account_number }} - @if ($item->status == 'On Progress') - {{ $item->status }} - @elseif ($item->status == 'Approved 1' || $item->status == 'Approved 2' || $item->status == 'Final Approved') - {{ $item->status }} - @else - {{ $item->status }} - @endif -
-
+

List Report Vehicle Running Cost

+
+ + + + + + + + + + + + + + + + + + @foreach ($forms as $item) + + + + + + + + + + + + {{-- AREA PREVIEW LAMPIRAN --}} + + + + + @endforeach + +
#No ExpensePengajuRegion / CabangNopol / BensinTgl. PenggunaanTgl. DibuatLiter / JarakTotal ApprovedBuktiStatus
{{ $loop->iteration }}{{ $item->expense_number }}{{ $item->user->name ?? '-' }} + {{ $item->user->region->cabang->region->name ?? '-' }} + {{ $item->user->cabang->cabang->name ?? '-' }} + +
{{ $item->nopol ?? '-' }}
+
{{ $item->tipe_bensin }}
+
{{ \Carbon\Carbon::parse($item->tanggal)->format('d M Y') }} +
{{ $item->created_at->format('d M Y') }}
+ {{ $item->created_at->format('H:i') }} WIB +
+
{{ $item->liter }} Liter
+
{{ $item->jarak }} km
+
{{ $item->approved_total ? number_format($item->approved_total, 0, ',', '.') : '-' }} + @php + $fileData = []; + $imgExts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp']; + + if (!empty($item->bukti_total)) { + $ext = strtolower(pathinfo($item->bukti_total, PATHINFO_EXTENSION)); + $fileData[] = ['url' => NextCloudHelper::getFileUrl($item->bukti_total), 'is_img' => in_array($ext, $imgExts)]; + } + + if ($item->attachments) { + foreach ($item->attachments as $att) { + if ($att->file_path) { + $ext = strtolower(pathinfo($att->file_path, PATHINFO_EXTENSION)); + $fileData[] = ['url' => NextCloudHelper::getFileUrl($att->file_path), 'is_img' => in_array($ext, $imgExts)]; + } + } + } + $uniqueFiles = collect($fileData)->unique('url')->values()->all(); + @endphp + + @if (count($uniqueFiles) > 0) + + @else + - + @endif + + @php + $badgeClass = match($item->status) { + 'On Progress' => 'badge-warning text-white', + 'Approved 1', 'Approved 2', 'Closed' => 'badge-success text-white', + 'Rejected' => 'badge-danger text-white', + default => 'badge-secondary' + }; + @endphp + {{ $item->status }} + + {{-- BLOK AUDIT TRAIL LOG REJECTED --}} + @if($item->status === 'Rejected') +
+ + Detail Penolakan: + + + Oleh: {{ $item->rejector->name ?? $item->rejected_by ?? 'System' }} + + @if($item->rejected_at) + + Waktu: {{ \Carbon\Carbon::parse($item->rejected_at)->translatedFormat('d M Y H:i') }} WIB + + @elseif($item->updated_at && $item->remarks) + + Waktu: {{ \Carbon\Carbon::parse($item->updated_at)->translatedFormat('d M Y H:i') }} WIB + + @endif + @if($item->remarks) + + Ket: "{{ $item->remarks }}" + + @endif +
+ @endif + + {{-- BLOK EXISTING FINAL APPROVED / CLOSED --}} + @if($item->final_approved_at && in_array($item->status, ['Approved 2', 'Closed'])) +
+ + Final Approved: + + + {{ \Carbon\Carbon::parse($item->final_approved_at)->translatedFormat('d M Y') }} + {{ \Carbon\Carbon::parse($item->final_approved_at)->format('H:i') }} WIB + +
+ @endif +
-
+
+ + {{-- MODAL PREVIEW & ZOOM --}} + + + @endsection @section('scripts') - + -@endsection + if(files && files.length > 0) { + files.forEach((file) => { + html += ` +
+
+
+
+ ${file.is_img + ? `` + : `

Dokumen Digital

` + } +
+
+ ${file.is_img + ? `` + : ` Buka PDF` + } + Download +
+
+
+
`; + }); + } + $('#containerPreview').html(html + ''); + $('#modalPreviewBukti').modal('show'); + }); + + function loadCabang(regionCode, selectedCabang = null) { + const cabangSelect = $('#cabang'); + if (regionCode) { + fetch(`/api/cabang-by-region/${regionCode}`) + .then(response => response.json()) + .then(data => { + cabangSelect.html(''); + data.forEach(cabang => { + const isSelected = (selectedCabang && cabang.code === selectedCabang) ? 'selected' : ''; + cabangSelect.append(``); + }); + }); + } + } + + $('#region').on('change', function() { loadCabang(this.value); }); + const currentRegion = '{{ request()->region }}'; + const currentCabang = '{{ request()->cabang }}'; + if (currentRegion) { loadCabang(currentRegion, currentCabang); } + }); + + function openFullImage(url) { + $('#imgFullDisplay').attr('src', url); + $('#modalImageFull').modal('show'); + } + +@endsection \ No newline at end of file diff --git a/resources/views/errors/503.blade.php b/resources/views/errors/503.blade.php new file mode 100644 index 0000000..36a7c48 --- /dev/null +++ b/resources/views/errors/503.blade.php @@ -0,0 +1,78 @@ + + + + + + 503 Service Unavailable - Sedang Perbaikan + + + +
+
+ Animasi sedang perbaikan + +
+ +

503

+

PEMELIHARAAN SISTEM

+ +

+ Mohon maaf, kami sedang melakukan Pemeliharaan Sistem. Tim IT sedang bekerja memulihkan layanan secepatnya! +

+ + +
+ + \ No newline at end of file diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 8c5cba7..e015a07 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -26,12 +26,12 @@ -