diff --git a/.agents/skills/ui-minimal-luxury/SKILL.md b/.agents/skills/ui-minimal-luxury/SKILL.md
index c59a849..bab696e 100644
--- a/.agents/skills/ui-minimal-luxury/SKILL.md
+++ b/.agents/skills/ui-minimal-luxury/SKILL.md
@@ -330,13 +330,15 @@ description: 定義 Star Cloud 管理後台的「極簡奢華風」設計規範
> [!TIP]
> 當遇到未定義的 UI 區塊時,優先參考 `admin.dashboard.blade.php` 的卡片與即時動態實作方式進行衍生。
-## 13. 加載指示器規範 (Loading Indicator Pattern)
+## 13. AJAX 交互與加載規範 (AJAX Interactions & Loaders)
-當使用 AJAX 進行局部頁面刷新或異步操作時,必須使用統一的「極奢華加載遮罩」。
+為了提供無刷新的 SPA 式流暢體驗,所有管理後台模組必須遵循統一的 AJAX 交互模式。
+
+### 13.1 極奢華多環加載器 (Premium Multi-Ring Loader)
+用於表格遮罩、局部更新或狀態切換。具備雙環旋轉與脈衝核心圖示。
-### 13.1 標準 HTML 結構
```html
-
+
```
-### 13.2 設計要點
-1. **多重動畫**:結合 `animate-spin` (不同速度/方向) 與 `animate-pulse`,營造出高科技且細膩的層次感。
-2. **顏色規範**:主要使用 `cyan-500` 作為亮點,搭配 `slate` 系列的半透明背景。
-3. **字體細節**:文字使用極細小尺寸 (`text-[10px]`)、極粗字重 (`font-black`) 並大幅度拉開字距 (`tracking-[0.4em]`)。
-4. **背景質感**:使用 `backdrop-blur-[1px]` 與 `bg-white/40` (或 `dark:bg-slate-900/40`),確保遮罩具有玻璃質感但不完全阻斷視線。
-5. **防止閃爍**: 必須加上 `x-cloak` 配合對應的 CSS 樣式。
-
-## 14. 頁面佈局與頁籤間距規範 (Page Layout & Tab Spacing)
-
-為了確保管理後台全站視覺的「高度整合感」與「緊湊性」,頁面頂部結構與頁籤導覽必須遵循以下間距規範。
-
-### 14.1 核心佈局間距 (Core Spacing Scale)
-- **根容器間距 (Root Space)**: 頂部 Header、Tabs 與內容區域之間的垂直間距統一使用 **`space-y-2`**。
-- **標題與描述 (Title & Subtitle)**: 描述文字應緊貼標題,使用 `mt-1`。
-- **頁籤外層間距 (Tabs Outer)**: 頁籤容器與 Header 之間應由根容器的 `space-y-2` 自然推開,不額外加 `mt`。
-
-### 14.2 頁籤導覽樣式 (Tab Navigation Styles)
-- **容器內距**: 使用 `p-1.5` 的圓角膠囊背景。
-- **按鈕內距**: 頁籤按鈕統一使用 **`px-8 py-3`**。
-- **內容間隙**: 頁籤下方內容區與頁籤列之間,建議不額外添加 `mt-6` 等大間距。若內容區域內部需要層次感,使用 `space-y-3` 即可。
-
-### 14.3 實作範例結構 (Reference Structure)
-```html
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-### 14.4 統計卡片間距 (Stats Grid Spacing)
-- **網格間距**: 頂部統計卡片網格 (Grid) 建議使用 **`gap-4`** (16px),避免使用過大的 `gap-6` 或 `gap-8` 導致版面鬆散。
-- **卡片內距**: 統計卡片內部統一使用 `p-6`。
-
-### 15. Premium AJAX Interactions & Loaders
-
-To maintain a seamless "SPA-like" experience without full-page reloads, all administrative modules must follow these AJAX patterns.
-
-#### 15.1 The Premium Multi-Ring Loader
-Used for table overlays and state transitions. It features a dual-spin ring with a pulsing core icon.
-
-```html
-
-
-```
-
-#### 15.2 Standard fetchPage Pattern
-All lists should implement the following `fetchPage` method in their `x-data` and wrap the dynamic content in `#ajax-content-container`.
+### 13.2 標準 fetchPage 實作模式
+所有清單頁面應在 `x-data` 中實作 `fetchPage` 方法,並將動態內容包裹於 `#ajax-content-container` 中。
```javascript
async fetchPage(url) {
@@ -470,7 +389,49 @@ async fetchPage(url) {
}
```
-#### 15.3 Triggering AJAX Navigation
-- **Forms**: Use `@submit.prevent="fetchPage($el.action + '?' + new URLSearchParams(new FormData($el)).toString())"`.
-- **Selects**: Use `onchange="this.dispatchEvent(new CustomEvent('ajax:navigate', { detail: { url: ... }, bubbles: true }))"`.
-- **Links/Pagination**: Use a delegated `@click` listener on the container or `@ajax:navigate.prevent.stop`.
\ No newline at end of file
+### 13.3 觸發 AJAX 導航
+- **搜尋表單**: 使用 `@submit.prevent="fetchPage($el.action + '?' + new URLSearchParams(new FormData($el)).toString())"`。
+- **下拉選單**: 使用 `onchange="this.dispatchEvent(new CustomEvent('ajax:navigate', { detail: { url: ... }, bubbles: true }))"`。
+- **分頁/連結**: 在容器上使用代理監聽,或直接調用 `fetchPage(url)`。
+
+## 14. 頁面佈局與頁籤間距規範 (Page Layout & Tab Spacing)
+
+為了確保管理後台全站視覺的「高度整合感」與「緊湊性」,頁面頂部結構與頁籤導覽必須遵循以下間距規範。
+
+### 14.1 核心佈局間距 (Core Spacing Scale)
+- **根容器間距 (Root Space)**: 頂部 Header、Tabs 與內容區域之間的垂直間距統一使用 **`space-y-2`**。
+- **標題與描述 (Title & Subtitle)**: 描述文字應緊貼標題,使用 `mt-1`。
+- **頁籤外層間距 (Tabs Outer)**: 頁籤容器與 Header 之間應由根容器的 `space-y-2` 自然推開,不額外加 `mt`。
+
+### 14.2 頁籤導覽樣式 (Tab Navigation Styles)
+- **容器內距**: 使用 `p-1.5` 的圓角膠囊背景。
+- **按鈕內距**: 頁籤按鈕統一使用 **`px-8 py-3`**。
+- **內容間隙**: 頁籤下方內容區與頁籤列之間,建議不額外添加 `mt-6` 等大間距。若內容區域內部需要層次感,使用 `space-y-3` 即可。
+
+### 14.3 實作範例結構 (Reference Structure)
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### 14.4 統計卡片間距 (Stats Grid Spacing)
+- **網格間距**: 頂部統計卡片網格 (Grid) 建議使用 **`gap-4`** (16px),避免使用過大的 `gap-6` 或 `gap-8` 導致版面鬆散。
+- **卡片內距**: 統計卡片內部統一使用 `p-6`。
\ No newline at end of file
diff --git a/app/Http/Controllers/Admin/CompanyController.php b/app/Http/Controllers/Admin/CompanyController.php
index 0a5389f..466b558 100644
--- a/app/Http/Controllers/Admin/CompanyController.php
+++ b/app/Http/Controllers/Admin/CompanyController.php
@@ -74,6 +74,13 @@ class CompanyController extends Controller
if (isset($validated['settings'])) {
$validated['settings']['enable_material_code'] = filter_var($validated['settings']['enable_material_code'] ?? false, FILTER_VALIDATE_BOOLEAN);
$validated['settings']['enable_points'] = filter_var($validated['settings']['enable_points'] ?? false, FILTER_VALIDATE_BOOLEAN);
+ $validated['settings']['tax_invoice'] = filter_var($validated['settings']['tax_invoice'] ?? false, FILTER_VALIDATE_BOOLEAN);
+ $validated['settings']['card_terminal'] = filter_var($validated['settings']['card_terminal'] ?? false, FILTER_VALIDATE_BOOLEAN);
+ $validated['settings']['scan_pay_esun'] = filter_var($validated['settings']['scan_pay_esun'] ?? false, FILTER_VALIDATE_BOOLEAN);
+ $validated['settings']['scan_pay_linepay'] = filter_var($validated['settings']['scan_pay_linepay'] ?? false, FILTER_VALIDATE_BOOLEAN);
+ $validated['settings']['shopping_cart'] = filter_var($validated['settings']['shopping_cart'] ?? false, FILTER_VALIDATE_BOOLEAN);
+ $validated['settings']['welcome_gift'] = filter_var($validated['settings']['welcome_gift'] ?? false, FILTER_VALIDATE_BOOLEAN);
+ $validated['settings']['cash_module'] = filter_var($validated['settings']['cash_module'] ?? false, FILTER_VALIDATE_BOOLEAN);
}
DB::transaction(function () use ($validated) {
@@ -217,6 +224,7 @@ class CompanyController extends Controller
*/
public function updateSettings(Request $request, Company $company)
{
+ \Log::info('updateSettings payload:', $request->all());
$settings = $request->input('settings', []);
$formattedSettings = [
diff --git a/config/api-docs.php b/config/api-docs.php
index d8ed015..e3bc1f9 100644
--- a/config/api-docs.php
+++ b/config/api-docs.php
@@ -460,7 +460,7 @@ return [
'description' => '當機台發生硬體故障(如卡貨、通訊中斷)時即時上報。',
'payload_parameters' => [
'tid' => ['type' => 'integer', 'description' => '貨道編號或任務 ID'],
- 'error_code' => ['type' => 'string', 'description' => '硬體錯誤代碼,詳見下方 B013 代碼表'],
+ 'error_code' => ['type' => 'string', 'description' => '硬體錯誤代碼 (如 0403 代表卡貨)'],
],
'payload_example' => [
'tid' => 12,
@@ -480,7 +480,7 @@ return [
'amount' => ['type' => 'number', 'description' => '交易金額 (create/invoice 時使用)'],
'slot_no' => ['type' => 'string', 'description' => '出貨貨道號碼'],
'invoice_no' => ['type' => 'string', 'description' => '發票號碼 (action 為 invoice 時必填)'],
- 'status' => ['type' => 'string', 'description' => '出貨結果狀態 (action 為 dispense 時必填,見狀態定義表)'],
+ 'status' => ['type' => 'string', 'description' => '出貨結果狀態 (action 為 dispense 時必填,如 success, failed)'],
],
'payload_example' => [
'action' => 'create',
@@ -596,7 +596,7 @@ return [
'description' => '機台執行完雲端指令後的主動回報。此回報極為重要,雲端將依此決定交易是否完成或需進行庫存回滾。',
'payload_parameters' => [
'command_id' => ['type' => 'string', 'description' => '對應下發時的指令 ID'],
- 'result' => ['type' => 'string', 'description' => '執行結果,見狀態定義表'],
+ 'result' => ['type' => 'string', 'description' => '執行結果,僅接受: success 或 failed'],
'stock' => ['type' => 'integer', 'description' => '執行指令後的最終剩餘庫存 (選填,通常出貨指令需帶上)'],
'message' => ['type' => 'string', 'description' => '額外的錯誤訊息 (選填)'],
],
@@ -608,39 +608,5 @@ return [
]
]
]
- ],
-
- 'status_definitions' => [
- [
- 'name' => 'B013: 硬體錯誤代碼 (MQTT Error Topic)',
- 'description' => '當機台透過 machine/{serial_no}/error 上報時,error_code 欄位所使用的代碼。',
- 'codes' => [
- ['code' => '0402', 'label' => '出貨成功', 'level' => 'info', 'description' => '商品成功掉落至取貨口。'],
- ['code' => '0403', 'label' => '貨道卡貨', 'level' => 'error', 'description' => '馬達轉動異常或偵測到卡貨,需人工排除。'],
- ['code' => '0202', 'label' => '貨道缺貨', 'level' => 'warning', 'description' => '偵測到實體貨道已無商品。'],
- ['code' => '0412', 'label' => '昇降機上升異常', 'level' => 'error', 'description' => '昇降馬達無法正確到達指定位置。'],
- ['code' => '0415', 'label' => '取貨門異常', 'level' => 'error', 'description' => '取貨門馬達損壞或感應器失效。'],
- ['code' => '5402', 'label' => '取貨門未關', 'level' => 'warning', 'description' => '取貨逾時或門片未正確閉合。'],
- ['code' => '5403', 'label' => '昇降系統故障', 'level' => 'error', 'description' => '昇降機通訊中斷或驅動板異常。'],
- ]
- ],
- [
- 'name' => '交易與指令執行狀態 (Transaction & Command)',
- 'description' => '用於 Transaction (dispense) 與 Command ACK 的執行結果判定。',
- 'codes' => [
- ['code' => 'success', 'label' => '成功', 'description' => '動作執行圓滿完成。'],
- ['code' => 'failed', 'label' => '失敗', 'description' => '動作執行失敗,雲端將視情況發起退款或記錄異常。'],
- ['code' => '1', 'label' => '成功 (數字型態)', 'description' => '部分舊型指令或 B602 使用的成功代碼。'],
- ['code' => '0', 'label' => '失敗 (數字型態)', 'description' => '部分舊型指令或 B602 使用的失敗代碼。'],
- ]
- ],
- [
- 'name' => '機台連線狀態 (Connection Status)',
- 'description' => '用於 machine/{serial_no}/status 主題,反映機台與 Broker 的連線情況。',
- 'codes' => [
- ['code' => 'online', 'label' => '在線', 'description' => '機台已成功連線至 MQTT Broker。'],
- ['code' => 'offline', 'label' => '離線', 'description' => '機台已斷開連線(通常由 LWT 遺囑訊息觸發)。'],
- ]
- ]
]
];
\ No newline at end of file
diff --git a/lang/en.json b/lang/en.json
index a9dae4a..b458396 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -291,6 +291,7 @@
"e.g., Drinks": "e.g., Drinks",
"e.g., Taipei Station": "e.g., Taipei Station",
"e.g., お飲み物": "e.g., O-Nomimono",
+ "ESUN": "ESUN",
"E.SUN QR Scan": "E.SUN QR Scan",
"E.SUN QR Scan Settings Description": "E.SUN Bank QR Scan Payment Settings",
"EASY_MERCHANT_ID": "EASY_MERCHANT_ID",
@@ -315,6 +316,7 @@
"Edit Slot": "Edit Slot",
"Edit Sub Account Role": "Edit Sub Account Role",
"Email": "Email",
+ "Enabled Features": "Enabled Features",
"Enable": "Enable",
"Enable Material Code": "Enable Material Code",
"Enable Points": "Enable Points",
@@ -709,6 +711,7 @@
"Please select a slot": "Please select a slot",
"PNG, JPG up to 2MB": "PNG, JPG up to 2MB",
"PNG, JPG, WEBP up to 10MB": "PNG, JPG, WEBP up to 10MB",
+ "Points": "Points",
"Point Rules": "Point Rules",
"Point Settings": "Point Settings",
"Points Rule": "Points Rule",
diff --git a/lang/ja.json b/lang/ja.json
index 3d786ad..e96f473 100644
--- a/lang/ja.json
+++ b/lang/ja.json
@@ -14,8 +14,16 @@
"Coin/Bill Acceptor": "コイン/紙幣機",
"System settings updated successfully.": "システム設定が正常に更新されました。",
"Update Settings": "設定を更新",
+ "Enabled Features": "有効な機能",
"Material Code": "素材コード",
"Points": "ポイント",
+ "Electronic Invoice": "電子翻訳",
+ "Card System": "カード決済システム",
+ "ESUN": "玉山銀行 (ESUN)",
+ "LinePay": "LinePay",
+ "Shopping Cart": "ショッピングカート",
+ "Welcome Gift": "登録特典",
+ "Cash Module": "現金決済モジュール",
"Please enter configuration name": "設定名を入力してください",
"Please select a company": "会社を選択してください",
"Warehouse Management": "倉庫管理",
diff --git a/lang/zh_TW.json b/lang/zh_TW.json
index 00671b3..25622de 100644
--- a/lang/zh_TW.json
+++ b/lang/zh_TW.json
@@ -317,6 +317,8 @@
"Edit Slot": "編輯貨道",
"Edit Sub Account Role": "編輯子帳號角色",
"Email": "電子郵件",
+ "Emblem / Label": "標章 / 標籤",
+ "Enabled Features": "啟用功能",
"Enable": "啟用",
"Enable Material Code": "啟用物料編號",
"Enable Points": "啟用點數規則",
@@ -718,6 +720,7 @@
"PNG, JPG, WEBP up to 10MB": "支援 PNG, JPG, WEBP 格式,且不超過 10MB",
"Point Rules": "點數規則",
"Point Settings": "點數設定",
+ "Points": "點數功能",
"Points Rule": "點數規則",
"Points Settings": "點數設定",
"Points toggle": "點數開關",
@@ -1260,6 +1263,7 @@
"Card System": "刷卡機系統",
"Credit Card / Contactless": "支援信用卡 / 感應支付",
"Scan Pay": "掃碼支付",
+ "ESUN": "玉山銀行",
"ESUN Scan Pay": "玉山掃碼支付",
"LinePay": "LinePay",
"Other Features": "其他功能",
diff --git a/resources/views/admin/ads/index.blade.php b/resources/views/admin/ads/index.blade.php
index d43eed8..984c33c 100644
--- a/resources/views/admin/ads/index.blade.php
+++ b/resources/views/admin/ads/index.blade.php
@@ -6,8 +6,17 @@ $baseRoute = 'admin.data-config.advertisements';
@endphp
@section('content')
-
{
+ if(value.trim()) url.searchParams.set(key, value);
+ else url.searchParams.delete(key);
+ });
+ fetchPage(url.pathname + url.search);
+ }"
data-ads="{{ json_encode($advertisements->items()) }}"
data-machines="{{ json_encode($machines) }}"
data-all-ads="{{ json_encode($allAds) }}"
@@ -59,7 +68,29 @@ $baseRoute = 'admin.data-config.advertisements';
-
+
+
+
+
+
+
{{ __('Loading Data') }}...
+
+
@@ -376,6 +407,7 @@ $baseRoute = 'admin.data-config.advertisements';
+ @keydown.escape.window="showLogPanel = false; showInventoryPanel = false"
+ @ajax:navigate.window.prevent="fetchPage($event.detail.url)"
+ @submit.prevent="if($event.target.method.toLowerCase() === 'get') {
+ const url = new URL($event.target.action, window.location.origin);
+ new FormData($event.target).forEach((value, key) => {
+ if(value.trim()) url.searchParams.set(key, value);
+ else url.searchParams.delete(key);
+ });
+ fetchPage(url.pathname + url.search);
+ }">
-
Syncing Data...
+
{{ __('Loading Data') }}...
+ :class="{ 'opacity-30 pointer-events-none transition-opacity duration-300': isLoadingTable }">
-
@@ -321,17 +333,17 @@
- {{
+ {{
__('Normal') }}
- {{
+ {{
__('Normal') }}
- {{
+ {{
__('Normal') }}
@@ -433,7 +445,7 @@
{{ __('Machine Logs') }}
+ class="mt-2 flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2 text-[12px] sm:text-sm text-slate-500 dark:text-slate-400 font-bold uppercase tracking-widest overflow-hidden">
—
@@ -458,7 +470,7 @@
-
{{ __('Loading Data') }}...
+
{{ __('Loading Data') }}...
@@ -569,13 +581,13 @@
|
+ class="px-6 py-4 text-[12px] font-black text-slate-400 uppercase tracking-[0.2em] border-b border-slate-200 dark:border-slate-700 w-40">
{{ __('Timestamp') }} |
+ class="px-6 py-4 text-[12px] font-black text-slate-400 uppercase tracking-[0.2em] border-b border-slate-200 dark:border-slate-700 w-24 text-center">
{{ __('Level') }} |
+ class="px-6 py-4 text-[12px] font-black text-slate-400 uppercase tracking-[0.2em] border-b border-slate-200 dark:border-slate-700">
{{ __('Message') }} |
@@ -594,7 +606,7 @@
'bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20': log.level === 'warning',
'bg-rose-500/10 text-rose-600 dark:text-rose-400 border-rose-500/20': log.level === 'error'
}"
- class="inline-flex items-center px-2 py-0.5 rounded-md border text-[10px] font-black uppercase tracking-wider">
+ class="inline-flex items-center px-2 py-0.5 rounded-md border text-[12px] font-black uppercase tracking-wider">
@@ -615,7 +627,7 @@
class="p-4 hover:bg-slate-50 dark:hover:bg-slate-800/30 transition-colors">
{{
+ class="text-[12px] font-bold text-slate-400 dark:text-slate-500 uppercase tracking-widest">{{
__('No matching logs found') }}
@@ -666,9 +678,9 @@
-
- /
-
+
+ /
+