From c43b6b0bec50615854b2ee6d462178ecf0f6186d Mon Sep 17 00:00:00 2001 From: Kazem Alghasi Date: Thu, 29 Oct 2026 15:56:30 +0330 Subject: [PATCH] fix(rate-import): fix file path resolution + redirect old import page to new 4-quadrant page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix 'File not found' error in ImportRates page by using Storage facade to resolve the actual path on the 'local' disk (which points to storage/app/private, not storage/app). - Deprecate the old ImportRatesPage (2-zone format) — hide from navigation and redirect anyone hitting its URL to /admin/rate-import. - Update the template download route to serve the new 4-quadrant IFNEX_Rate_Template.xlsx from public/templates/ instead of the old ShippingRatesTemplateExport (2-zone format). - Update rate-import.blade.php to use the named route for the download link. - Add public/templates/IFNEX_Rate_Template.xlsx for direct web access. --- .../app/Filament/Pages/ImportRatesPage.php | 19 ++++++++++-- .../RateImportResource/Pages/ImportRates.php | 29 +++++++++++------- .../Resources/views/rate-import.blade.php | 11 +++++-- .../public/templates/IFNEX_Rate_Template.xlsx | Bin 0 -> 15246 bytes 04_Laravel/routes/web.php | 18 +++++++++-- 5 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 04_Laravel/public/templates/IFNEX_Rate_Template.xlsx diff --git a/04_Laravel/app/Filament/Pages/ImportRatesPage.php b/04_Laravel/app/Filament/Pages/ImportRatesPage.php index 15168bc..c27f806 100644 --- a/04_Laravel/app/Filament/Pages/ImportRatesPage.php +++ b/04_Laravel/app/Filament/Pages/ImportRatesPage.php @@ -12,20 +12,35 @@ use Filament\Notifications\Notification; use Filament\Pages\Page; use Maatwebsite\Excel\Facades\Excel; +/** + * ⚠️ DEPRECATED — این صفحه قدیمی برای فرمت ۲-زونی است. + * + * با معماری ۴-رُبعی جدید، ایمپورت نرخ‌ها از طریق Resource جدید انجام می‌شود: + * App\Filament\Resources\RateImportResource → URL: /admin/rate-import + * + * این صفحه فقط برای backward-compatibility باقی مانده و در منوی ناوبری + * نمایش داده نمی‌شود. اگر کاربر مستقیم URL قدیمی را باز کرد، به صفحه جدید + * ریدایرکت می‌شود. + */ class ImportRatesPage extends Page implements HasForms { use InteractsWithForms; protected static ?string $navigationIcon = 'heroicon-o-arrow-up-on-square'; - protected static ?string $navigationLabel = 'ایمپورت زون ها'; + protected static ?string $navigationLabel = 'ایمپورت زون ها (قدیمی)'; protected static ?int $navigationSort = 8; protected static string $view = 'filament.pages.import-rates'; + /** ❌ این صفحه از منوی ناوبری پنهان است — از /admin/rate-import استفاده کنید. */ + protected static bool $shouldRegisterNavigation = false; + public ?array $formData = []; public function mount(): void { - $this->form->fill(); + // ریدایرکت به صفحه جدید ایمپورت نرخ‌ها (۴-رُبعی) + redirect()->to('/admin/rate-import')->send(); + exit; } public function form(Form $form): Form diff --git a/04_Laravel/app/Filament/Resources/RateImportResource/Pages/ImportRates.php b/04_Laravel/app/Filament/Resources/RateImportResource/Pages/ImportRates.php index 5067451..11e716f 100644 --- a/04_Laravel/app/Filament/Resources/RateImportResource/Pages/ImportRates.php +++ b/04_Laravel/app/Filament/Resources/RateImportResource/Pages/ImportRates.php @@ -13,6 +13,7 @@ use Filament\Forms\Contracts\HasForms; use Filament\Forms\Form; use Filament\Notifications\Notification; use Filament\Resources\Pages\Page; +use Illuminate\Support\Facades\Storage; use Livewire\Features\SupportFileUploads\TemporaryUploadedFile; /** @@ -85,17 +86,23 @@ class ImportRates extends Page implements HasForms $uploadedPath = $state['uploaded_file']; - // FileUpload on 'local' disk returns a relative path inside storage/app/ - $filePath = is_string($uploadedPath) - ? storage_path('app/' . $uploadedPath) - : ($uploadedPath instanceof TemporaryUploadedFile - ? $uploadedPath->getRealPath() - : (string) $uploadedPath); + // FileUpload روی دیسک 'local' ذخیره می‌شود که در config/filesystems.php + // به storage_path('app/private') اشاره دارد. مسیر واقعی را از خود دیسک + // می‌پرسیم تا با تغییر کانفیگ هم درست کار کند. + $filePath = null; + if (is_string($uploadedPath)) { + // مسیر نسبی روی دیسک 'local' — Storage مسیر کامل را برمی‌گرداند + $filePath = Storage::disk('local')->path($uploadedPath); + } elseif ($uploadedPath instanceof TemporaryUploadedFile) { + $filePath = $uploadedPath->getRealPath(); + } else { + $filePath = (string) $uploadedPath; + } - if (! file_exists($filePath)) { + if (! $filePath || ! file_exists($filePath)) { Notification::make() ->title('File not found after upload') - ->body('Path: ' . $filePath) + ->body('Path checked: ' . $filePath) ->danger() ->send(); return; @@ -111,8 +118,8 @@ class ImportRates extends Page implements HasForms "✓ Imported: %d | Updated: %d | Skipped: %d | Zone rows: %d", $summary['imported'] ?? 0, $summary['updated'] ?? 0, - $summary['skipped'] ?? 0, - $summary['zones_updated'] ?? 0, + $summary['skipped'] ?? 0, + $summary['zones_updated'] ?? 0, ); if (! empty($summary['errors'])) { @@ -139,4 +146,4 @@ class ImportRates extends Page implements HasForms ->send(); } } -} +} \ No newline at end of file diff --git a/04_Laravel/app/Filament/Resources/views/rate-import.blade.php b/04_Laravel/app/Filament/Resources/views/rate-import.blade.php index 30dd865..b0f02a2 100644 --- a/04_Laravel/app/Filament/Resources/views/rate-import.blade.php +++ b/04_Laravel/app/Filament/Resources/views/rate-import.blade.php @@ -32,10 +32,15 @@
-

Download template

+

دانلود قالب خالی

- Need the blank template? Download IFNEX_Rate_Template.xlsx + اگر قالب خالی را می‌خواهید، + دانلود IFNEX_Rate_Template.xlsx + (فرمت ۴-رُبعی: Import Rate، Export Rate، Zone). +

+

+ ⚠️ توجه: فایل اصلی مشتری (با داده‌های نرخ واقعی) را مستقیم آپلود کنید — این قالب فقط فرمت خالی است.

- + \ No newline at end of file diff --git a/04_Laravel/public/templates/IFNEX_Rate_Template.xlsx b/04_Laravel/public/templates/IFNEX_Rate_Template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..70b129c13182a994bd10b483e00cb6dc44259e03 GIT binary patch literal 15246 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 Ls#@^h{`Y?X0T)_- literal 0 HcmV?d00001 diff --git a/04_Laravel/routes/web.php b/04_Laravel/routes/web.php index 1d7b0da..abd7df0 100644 --- a/04_Laravel/routes/web.php +++ b/04_Laravel/routes/web.php @@ -48,6 +48,20 @@ Route::get('/payment-result', function (Illuminate\Http\Request $request) { 'message' => $message, ]); })->name('payment.result'); + +// دانلود قالب خالی نرخ‌ها (IFNEX_Rate_Template.xlsx — فرمت ۴-رُبعی جدید) +// فایل واقعی در public/templates قرار دارد تا مستقیم از طریق وب هم قابل دسترس باشد. Route::get('/download-rate-template', function () { - return (new \App\Exports\ShippingRatesTemplateExport)->download('IFNEX_Rate_Template.xlsx'); -})->middleware(['auth', 'panel:admin'])->name('download.rate-template'); \ No newline at end of file + $file = public_path('templates/IFNEX_Rate_Template.xlsx'); + if (! file_exists($file)) { + abort(404, 'Template file not found. Please add public/templates/IFNEX_Rate_Template.xlsx'); + } + return response()->download($file, 'IFNEX_Rate_Template.xlsx', [ + 'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ]); +})->name('download.rate-template'); + +// alias کوتاه‌تر که از صفحه /admin/rate-import لینک شده +Route::get('/admin/rate-import/template', function () { + return redirect()->route('download.rate-template'); +})->name('admin.rate-import.template'); \ No newline at end of file