diff --git a/app/Http/Controllers/Admin/ProductCategoryController.php b/app/Http/Controllers/Admin/ProductCategoryController.php new file mode 100644 index 0000000..64ea698 --- /dev/null +++ b/app/Http/Controllers/Admin/ProductCategoryController.php @@ -0,0 +1,167 @@ +user(); + $query = ProductCategory::with(['translations']); + + if ($user->isSystemAdmin() && $request->filled('company_id')) { + $query->where('company_id', $request->company_id); + } + + $categories = $query->latest()->get(); + + if ($request->wantsJson()) { + return response()->json([ + 'success' => true, + 'data' => $categories + ]); + } + + return redirect()->route('admin.data-config.products.index', ['tab' => 'categories']); + } + + /** + * 儲存新分類 + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'names.zh_TW' => 'required|string|max:255', + 'names.en' => 'nullable|string|max:255', + 'names.ja' => 'nullable|string|max:255', + 'company_id' => 'nullable|exists:companies,id', + ]); + + try { + DB::beginTransaction(); + + $dictKey = Str::uuid()->toString(); + $company_id = (auth()->user()->isSystemAdmin() && $request->filled('company_id')) + ? $request->company_id + : auth()->user()->company_id; + + // 儲存多語系翻譯 + foreach ($request->names as $locale => $value) { + if (empty($value)) continue; + Translation::withoutGlobalScopes()->create([ + 'group' => 'category', + 'key' => $dictKey, + 'locale' => $locale, + 'value' => $value, + 'company_id' => $company_id, + ]); + } + + $category = ProductCategory::create([ + 'company_id' => $company_id, + 'name' => $request->names['zh_TW'] ?? (collect($request->names)->first() ?? 'Untitled'), + 'name_dictionary_key' => $dictKey, + ]); + + DB::commit(); + + return redirect()->back()->with('success', __('Category created successfully')); + + } catch (\Exception $e) { + DB::rollBack(); + return redirect()->back()->with('error', $e->getMessage())->withInput(); + } + } + + /** + * 更新分類 + */ + public function update(Request $request, $id) + { + $category = ProductCategory::findOrFail($id); + + $validated = $request->validate([ + 'names.zh_TW' => 'required|string|max:255', + 'names.en' => 'nullable|string|max:255', + 'names.ja' => 'nullable|string|max:255', + ]); + + try { + DB::beginTransaction(); + + $dictKey = $category->name_dictionary_key ?: Str::uuid()->toString(); + $company_id = $category->company_id; + + foreach ($request->names as $locale => $value) { + if (empty($value)) { + Translation::withoutGlobalScopes()->where([ + 'group' => 'category', + 'key' => $dictKey, + 'locale' => $locale + ])->delete(); + continue; + } + + Translation::withoutGlobalScopes()->updateOrCreate( + [ + 'group' => 'category', + 'key' => $dictKey, + 'locale' => $locale, + ], + [ + 'value' => $value, + 'company_id' => $company_id, + ] + ); + } + + $category->update([ + 'name' => $request->names['zh_TW'] ?? $category->name, + 'name_dictionary_key' => $dictKey, + ]); + + DB::commit(); + + return redirect()->back()->with('success', __('Category updated successfully')); + + } catch (\Exception $e) { + DB::rollBack(); + return redirect()->back()->with('error', $e->getMessage())->withInput(); + } + } + + /** + * 刪除分類 + */ + public function destroy($id) + { + try { + $category = ProductCategory::findOrFail($id); + + // 檢查是否已有商品使用此分類 + if ($category->products()->count() > 0) { + return redirect()->back()->with('error', __('Cannot delete category that has products. Please move products first.')); + } + + if ($category->name_dictionary_key) { + Translation::withoutGlobalScopes()->where('group', 'category')->where('key', $category->name_dictionary_key)->delete(); + } + + $category->delete(); + + return redirect()->back()->with('success', __('Category deleted successfully')); + } catch (\Exception $e) { + return redirect()->back()->with('error', $e->getMessage()); + } + } +} diff --git a/app/Http/Controllers/Admin/ProductController.php b/app/Http/Controllers/Admin/ProductController.php index 0d950f0..9d1d6cf 100644 --- a/app/Http/Controllers/Admin/ProductController.php +++ b/app/Http/Controllers/Admin/ProductController.php @@ -19,7 +19,7 @@ class ProductController extends Controller public function index(Request $request) { $user = auth()->user(); - $query = Product::with(['category', 'translations', 'company']); + $query = Product::with(['category.translations', 'translations', 'company']); // 搜尋 if ($request->filled('search')) { @@ -47,7 +47,7 @@ class ProductController extends Controller } $products = $query->latest()->paginate($per_page)->withQueryString(); - $categories = ProductCategory::all(); + $categories = ProductCategory::with('translations')->get(); $companies = $user->isSystemAdmin() ? Company::all() : collect(); // 系統管理員在過濾特定公司時,應顯示該公司的功能開關 (如物料代碼、點數規則) @@ -68,7 +68,7 @@ class ProductController extends Controller public function create(Request $request) { $user = auth()->user(); - $categories = ProductCategory::all(); + $categories = ProductCategory::with('translations')->get(); $companies = $user->isSystemAdmin() ? Company::all() : collect(); // If system admin, check if company_id is provided in URL to get settings @@ -94,7 +94,7 @@ class ProductController extends Controller ->where('key', $product->name_dictionary_key) ->get() ); - $categories = ProductCategory::all(); + $categories = ProductCategory::with('translations')->get(); $companies = $user->isSystemAdmin() ? Company::all() : collect(); // Use the product's company settings for editing diff --git a/app/Models/Product/ProductCategory.php b/app/Models/Product/ProductCategory.php index 2e46a2e..02c3f7e 100644 --- a/app/Models/Product/ProductCategory.php +++ b/app/Models/Product/ProductCategory.php @@ -17,6 +17,11 @@ class ProductCategory extends Model 'name_dictionary_key', ]; + /** + * 自動附加到 JSON/陣列輸出 + */ + protected $appends = ['localized_name']; + public function products() { return $this->hasMany(Product::class, 'category_id'); @@ -30,4 +35,26 @@ class ProductCategory extends Model return $this->hasMany(\App\Models\System\Translation::class, 'key', 'name_dictionary_key') ->where('group', 'category'); } + + /** + * 取得當前語系的商品名稱。 + * 回退順序:當前語系 → zh_TW → name 欄位 + */ + public function getLocalizedNameAttribute(): string + { + if ($this->relationLoaded('translations') && $this->translations->isNotEmpty()) { + $locale = app()->getLocale(); + // 先找當前語系 + $translation = $this->translations->firstWhere('locale', $locale); + if ($translation) { + return $translation->value; + } + // 回退至 zh_TW + $fallback = $this->translations->firstWhere('locale', 'zh_TW'); + if ($fallback) { + return $fallback->value; + } + } + return $this->name ?? ''; + } } diff --git a/lang/en.json b/lang/en.json index 48bda6f..c070498 100644 --- a/lang/en.json +++ b/lang/en.json @@ -10,16 +10,17 @@ "APP Version": "APP Version", "APP_ID": "APP_ID", "APP_KEY": "APP_KEY", - "Account": "帳號", + "Account": "Account", "Account :name status has been changed to :status.": "Account :name status has been changed to :status.", + "Account Info": "Account Info", "Account Management": "Account Management", - "Account Name": "帳號姓名", + "Account Name": "Account Name", "Account Settings": "Account Settings", "Account Status": "Account Status", "Account created successfully.": "Account created successfully.", "Account deleted successfully.": "Account deleted successfully.", "Account updated successfully.": "Account updated successfully.", - "Account:": "帳號:", + "Account:": "Account:", "Accounts / Machines": "Accounts / Machines", "Action": "Action", "Actions": "Actions", @@ -37,10 +38,11 @@ "Admin display name": "Admin display name", "Administrator": "Administrator", "Advertisement Management": "Advertisement Management", + "Affiliated Company": "Affiliated Company", "Affiliated Unit": "Company Name", "Affiliation": "Company Name", "Alert Summary": "Alert Summary", - "Alerts": "中心告警", + "Alerts": "Alerts", "Alerts Pending": "Alerts Pending", "All": "All", "All Affiliations": "All Companies", @@ -203,13 +205,24 @@ "EASY_MERCHANT_ID": "EASY_MERCHANT_ID", "ECPay Invoice": "ECPay Invoice", "ECPay Invoice Settings Description": "ECPay Electronic Invoice Settings", + "Type to search or leave blank for system defaults.": "Type to search or leave blank for system defaults.", + "Select Company (Default: System)": "Select Company (Default: System)", + "Search Company Title...": "Search Company Title...", + "System Default (Common)": "System Default (Common)", + "Category Name (zh_TW)": "Category Name (Traditional Chinese)", + "Category Name (en)": "Category Name (English)", + "Category Name (ja)": "Category Name (Japanese)", + "e.g., Beverage": "e.g., Beverage", + "e.g., Drinks": "e.g., Drinks", + "e.g., お飲み物": "e.g., O-Nomimono", "Edit": "Edit", + "Edit Category": "Edit Category", "Edit Account": "Edit Account", "Edit Customer": "Edit Customer", "Edit Expiry": "Edit Expiry", "Edit Machine": "Edit Machine", "Edit Machine Model": "Edit Machine Model", - "Edit Machine Settings": "編輯機台設定", + "Edit Machine Settings": "Edit Machine Settings", "Edit Payment Config": "Edit Payment Config", "Edit Product": "Edit Product", "Edit Role": "Edit Role", @@ -867,5 +880,13 @@ "Ad Settings": "Ad Settings", "System Default (All Companies)": "System Default (All Companies)", "No materials available": "No materials available", - "Search...": "Search..." + "Search...": "Search...", + "Add Category": "Add Category", + "Category Management": "Category Management", + "Category Name": "Category Name", + "Manage your catalog, categories, and inventory settings.": "Manage your catalog, categories, and inventory settings.", + "Multilingual Names": "Multilingual Names", + "Barcode / Material": "Barcode / Material", + "Product List": "Product List", + "Product Count": "Product Count" } \ No newline at end of file diff --git a/lang/ja.json b/lang/ja.json index bf0dc40..757f174 100644 --- a/lang/ja.json +++ b/lang/ja.json @@ -96,6 +96,10 @@ "Buyout": "買取", "Cancel": "キャンセル", "Cancel Purchase": "購入キャンセル", + "Category": "カテゴリ", + "Category Name (zh_TW)": "カテゴリ名 (中国語(繁体字))", + "Category Name (en)": "カテゴリ名 (英語)", + "Category Name (ja)": "カテゴリ名 (日本語)", "Cannot Delete Role": "ロールを削除できません", "Cannot change Super Admin status.": "スーパー管理者のステータスは変更できません。", "Cannot delete company with active accounts.": "有効なアカウントを持つ顧客を削除できません。", @@ -105,7 +109,6 @@ "Card Reader No": "カードリーダー番号", "Card Reader Restart": "カードリーダー再起動", "Card Reader Seconds": "カードリーダー秒数", - "Category": "カテゴリ", "Change": "変更", "Change Stock": "小銭在庫", "Channel Limits": "スロット上限", @@ -195,7 +198,14 @@ "Dispense Failed": "出庫失敗", "Dispense Success": "出庫成功", "Dispensing": "出庫中", - "E.SUN QR Scan": "玉山銀行 QR スキャン", + "Type to search or leave blank for system defaults.": "キーワードで検索するか、システムデフォルトの場合は空白のままにします。", + "Select Company (Default: System)": "会社を選択 (デフォルト:システム)", + "Search Company Title...": "会社名を検索...", + "System Default (Common)": "システムデフォルト (共通)", + "e.g., Beverage": "例:飲料", + "e.g., Drinks": "例:飲料 (英語)", + "e.g., お飲み物": "例:お飲み物", + "E.SUN QR Scan": "玉山QR決済", "E.SUN QR Scan Settings Description": "玉山銀行 QR スキャン決済設定", "EASY_MERCHANT_ID": "悠遊付 加盟店ID", "ECPay Invoice": "ECPay 電子発票", @@ -870,5 +880,14 @@ "Ad Settings": "広告設定", "System Default (All Companies)": "システムデフォルト(すべての会社)", "No materials available": "利用可能な素材がありません", - "Search...": "検索..." + "Search...": "検索...", + "Add Category": "新しいカテゴリー", + "Category Management": "カテゴリー管理", + "Category Name": "カテゴリー名", + "Edit Category": "カテゴリー編集", + "Manage your catalog, categories, and inventory settings.": "型録、カテゴリー、および在庫設定を管理します。", + "Multilingual Names": "多言語名", + "Barcode / Material": "バーコード / 材料", + "Product List": "商品リスト", + "Product Count": "商品数" } \ No newline at end of file diff --git a/lang/zh_TW.json b/lang/zh_TW.json index 90f76a8..793a740 100644 --- a/lang/zh_TW.json +++ b/lang/zh_TW.json @@ -26,6 +26,7 @@ "Actions": "操作", "Active": "使用中", "Active Status": "啟用狀態", + "Add Category": "新增分類", "Add Account": "新增帳號", "Add Customer": "新增客戶", "Add Machine": "新增機台", @@ -40,6 +41,7 @@ "Admin display name": "管理員顯示名稱", "Administrator": "管理員", "Advertisement Management": "廣告管理", + "Affiliated Company": "公司名稱", "Affiliated Unit": "公司名稱", "Affiliation": "所屬單位", "Alert Summary": "告警摘要", @@ -111,6 +113,9 @@ "Card Reader Restart": "卡機重啟", "Card Reader Seconds": "刷卡機秒數", "Category": "類別", + "Category Name (zh_TW)": "分類名稱 (繁體中文)", + "Category Name (en)": "分類名稱 (英文)", + "Category Name (ja)": "分類名稱 (日文)", "Change": "更換", "Change Stock": "零錢庫存", "Channel Limits": "貨道上限", @@ -201,15 +206,22 @@ "Disable Product Confirmation": "停用商品確認", "Disabled": "已停用", "Discord Notifications": "Discord通知", - "Dispense Failed": "出貨失敗", "Dispense Success": "出貨成功", "Dispensing": "出貨", + "Type to search or leave blank for system defaults.": "輸入關鍵字搜尋,或留空以使用系統預設。", + "Select Company (Default: System)": "選擇公司 (預設:系統)", + "Search Company Title...": "搜尋公司名稱...", + "System Default (Common)": "系統預設 (通用)", + "e.g., Beverage": "例如:飲料", + "e.g., Drinks": "例如:Drinks", + "e.g., お飲み物": "例如:お飲み物", "E.SUN QR Scan": "玉山銀行標籤支付", "E.SUN QR Scan Settings Description": "玉山銀行掃碼支付設定", "EASY_MERCHANT_ID": "悠遊付 商店代號", "ECPay Invoice": "綠界電子發票", "ECPay Invoice Settings Description": "綠界科技電子發票設定", "Edit": "編輯", + "Edit Category": "編輯分類", "Edit Account": "編輯帳號", "Edit Customer": "編輯客戶", "Edit Expiry": "編輯效期", @@ -510,6 +522,8 @@ "Product Info": "商品資訊", "Product Management": "商品管理", "Product Name (Multilingual)": "商品名稱 (多語系)", + "Product Count": "商品數量", + "Product List": "商品清單", "Product Reports": "商品報表", "Product Status": "商品狀態", "Product created successfully": "商品已成功建立", @@ -798,6 +812,9 @@ "menu.machines": "機台管理", "menu.machines.list": "機台列表", "menu.machines.maintenance": "維修管理單", + "Manage your catalog, categories, and inventory settings.": "管理您的商品型錄、分類及庫存設定。", + "Multilingual Names": "多語系名稱", + "Barcode / Material": "條碼 / 物料編碼", "menu.machines.permissions": "機台權限", "menu.machines.utilization": "機台嫁動率", "menu.members": "會員管理", @@ -894,5 +911,6 @@ "Ad Settings": "廣告設置", "System Default (All Companies)": "系統預設 (所有公司)", "No materials available": "沒有可用的素材", - "Search...": "搜尋..." + "Search...": "搜尋...", + "Category Management": "分類管理" } \ No newline at end of file diff --git a/resources/views/admin/ads/partials/ad-modal.blade.php b/resources/views/admin/ads/partials/ad-modal.blade.php index 6bcf68c..b84af01 100644 --- a/resources/views/admin/ads/partials/ad-modal.blade.php +++ b/resources/views/admin/ads/partials/ad-modal.blade.php @@ -10,18 +10,18 @@
-
-
+
-
+
-

+

{{ __('Manage your ad material details') }}

-
@@ -29,7 +29,7 @@ method="POST" enctype="multipart/form-data" @submit.prevent="submitAdForm" - class="px-8 pt-4 pb-8 space-y-4"> + class="space-y-6"> @csrf