[FEAT] 儀表板 UI 大改造、中文化與項目開發規範同步 (含深色模式修復)
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 58s
All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 58s
This commit is contained in:
@@ -74,5 +74,13 @@ trigger: always_on
|
||||
* **Demo 環境 (對應 `demo` 分支)**:
|
||||
* 透過 `deploy-demo.yaml`,合併或推送到 `demo` 分支會自動部署至 `demo-cloud.taiwan-star.com.tw`。
|
||||
* 登入伺服器查修:`ssh gitea_work`,路徑為 `/var/www/star-cloud-demo`。
|
||||
* **Production 環境 (對應 `main` 分支)**:
|
||||
* 透過 `deploy-prod.yaml`,推進到 `main` 會自動部署至正式站。
|
||||
|
||||
## 9. 瀏覽器測試規範 (Browser Testing)
|
||||
當需要進行瀏覽器自動化測試或手動驗證時,必須遵守以下連線資訊:
|
||||
|
||||
* **本地測試網址**:`http://localhost:8090/` (注意:非 8000 或 8080)
|
||||
* **預設管理員帳號**:`admin`
|
||||
* **預設管理員密碼**:`password`
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 在執行 `open_browser_url` 或進行 E2E 測試時,請務必優先確認 Port 是否為 `8090`,以避免連線至錯誤的服務環境。
|
||||
@@ -6,7 +6,7 @@ trigger: always_on
|
||||
|
||||
本文件確保 AI 助手在對話中能**主動辨識**需要參照技能 (Skill) 的時機。
|
||||
Skills 位於 `.agents/skills/`,採漸進式揭露以節省 Token。
|
||||
**若對話內容命中以下任一觸發條件,必須先使用 `view_file` 讀取對應的 `SKILL.md` 後再進行作業。**
|
||||
**若對話內容命中以下任一觸發條件,必須先使用 `view_file` 讀取對應的 `SKILL.md` 後標記為 active 再進行作業。**
|
||||
|
||||
---
|
||||
|
||||
@@ -15,6 +15,7 @@ Skills 位於 `.agents/skills/`,採漸進式揭露以節省 Token。
|
||||
| 觸發詞 / 情境 | 對應 Skill | 路徑 |
|
||||
|---|---|---|
|
||||
| 機台通訊, IoT, 日誌上報, Log Ingestion, 異步隊列, Queue, Heartbeat, 心跳發報 | **IoT 通訊與高併發處理規範** | `.agents/skills/iot-communication/SKILL.md` |
|
||||
| 介面, UI, 佈局, CSS, Tailwind, 奢華, 深色模式, Light Mode, Dark Mode, Blade, 樣式, 間距, 陰影, 動畫 | **極簡奢華風 UI 實作規範** | `.agents/skills/ui-minimal-luxury/SKILL.md` |
|
||||
|
||||
---
|
||||
|
||||
@@ -22,6 +23,10 @@ Skills 位於 `.agents/skills/`,採漸進式揭露以節省 Token。
|
||||
|
||||
以下場景**無論對話中是否出現觸發詞**,都必須主動載入對應 Skill:
|
||||
|
||||
### 🔴 新增或修改頁面 (Views/Blade) 時
|
||||
必須讀取:
|
||||
1. **ui-minimal-luxury** — 確保符合極簡奢華風視覺與互動規範
|
||||
|
||||
### 🔴 新增機台通訊 API 端點時
|
||||
必須讀取:
|
||||
1. **iot-communication** — 決定是否使用異步隊列流程
|
||||
|
||||
90
.agents/skills/ui-minimal-luxury/SKILL.md
Normal file
90
.agents/skills/ui-minimal-luxury/SKILL.md
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: 極簡奢華風 UI 實作規範 (Minimal Luxury UI)
|
||||
description: 定義 Star Cloud 管理後台的「極簡奢華風」設計規範,包含 CSS Tokens、常用組件樣式、動畫效果與互動模式,確保全站 15+ 模組的視覺一致性。
|
||||
---
|
||||
|
||||
# 極簡奢華風 UI 實作規範 (Minimal Luxury UI)
|
||||
|
||||
本文件定義了 Star Cloud 專案的核心視覺語言。所有新頁面與組件開發必須嚴格遵守此規範。
|
||||
|
||||
## 1. 核心設計令牌 (Design Tokens)
|
||||
|
||||
### 色彩系統 (CSS Variables)
|
||||
位於 `resources/css/app.css`:
|
||||
- `--color-luxury-deep`: `#0f172a` (深色背景)
|
||||
- `--color-luxury-card`: `#1e293b` (卡片背景)
|
||||
- `--color-accent`: `#06b6d4` (青色點綴,適用於按鈕與標示)
|
||||
|
||||
### 字體 (Typography)
|
||||
- **內文字體**: `Plus Jakarta Sans`
|
||||
- **標題/顯示字體**: `Outfit`
|
||||
- **特性**: 標題需帶有 `letter-spacing: -0.02em` 以增強精密感。
|
||||
|
||||
## 2. 核心組件樣式
|
||||
|
||||
### 豪華卡片 (Luxury Card)
|
||||
```html
|
||||
<div class="luxury-card p-6 rounded-2xl animate-luxury-in">
|
||||
<!-- 內容 -->
|
||||
</div>
|
||||
```
|
||||
- **特效**: 懸停時帶有 Y 軸平移與深度投影。
|
||||
|
||||
### 側邊導覽項 (Luxury Nav Item)
|
||||
```html
|
||||
<a href="#" class="luxury-nav-item active">
|
||||
<i class="lucide-icon"></i>
|
||||
<span>節點名稱</span>
|
||||
</a>
|
||||
```
|
||||
- **啟用狀態**: 左側帶有圓角直條指示器,並輔以青色發光陰影。
|
||||
|
||||
### 按鈕組件 (Buttons)
|
||||
- **Primary**: `.btn-luxury-primary` (青色漸層,適用於建立、儲存)
|
||||
- **Secondary**: `.btn-luxury-secondary` (白色/深色背景,帶邊框,適用於編輯、篩選)
|
||||
- **Ghost**: `.btn-luxury-ghost` (無背景,適用於取消、查看更多)
|
||||
|
||||
```html
|
||||
<!-- Primary -->
|
||||
<button class="btn-luxury-primary">
|
||||
<i class="lucide-plus size-4"></i>
|
||||
<span>建立新機台</span>
|
||||
</button>
|
||||
|
||||
<!-- Ghost -->
|
||||
<button class="btn-luxury-ghost">取消</button>
|
||||
```
|
||||
|
||||
## 3. 動畫與互動
|
||||
|
||||
### 進場動畫
|
||||
- **`.animate-luxury-in`**: 所有的主內容區域或卡片在頁面載入時,應具備由下而上的淡入效果。
|
||||
|
||||
### Alpine.js 互動模式 (以時間選擇器為例)
|
||||
- **互動原則**: 點擊觸發下拉選單時,必須使用 `x-transition` 且帶有 `scale` 偏移。
|
||||
- **樣式要求**: 選單背景需使用玻璃擬態 (Glassmorphism) 或帶透明度的深色背景。
|
||||
|
||||
## 4. UI 檢查清單 (AI 助手執行前必讀)
|
||||
- [ ] 是否使用了正確的 `rounded-2xl` (或更圓) 的導角?
|
||||
- [ ] 所有的圖示是否一致使用 `lucide-react` 風格?
|
||||
- [ ] 卡片是否有適當的間距 (通常為 `p-6`)?
|
||||
- [ ] 文字色階是否符合:標題 (slate-900/white)、副標 (slate-500/slate-400)?
|
||||
|
||||
## 5. 開發注意事項 (Important Notes)
|
||||
|
||||
### 技術限制備忘
|
||||
- **CSS 編譯**: 複雜的 `box-shadow` 或漸層應直接寫原生 CSS 屬性,避免在 `@apply` 中使用帶空格的數值導致編譯失敗(詳見 KI: `tailwind-luxury-ui-patterns`)。
|
||||
- **深色模式**: 互動式按鈕在深色模式下必須強化文字亮度(`dark:text-white`),並輔以青色發光效果。
|
||||
|
||||
### 即時動態呈現規範
|
||||
- **格式**: `#機台編號 動作內容` (例如 `#V-001 執行出貨`)。
|
||||
- **脈絡**: 必須呈現相對時間與機台位置。
|
||||
|
||||
---
|
||||
> [!IMPORTANT]
|
||||
> **開發新功能前,必須確認 `app.css` 中的 `.btn-luxury-*` 系列組件是否滿足需求。**
|
||||
> 嚴禁在 Blade 中寫入大量重複的 `bg-indigo-600` 等舊式類別。
|
||||
|
||||
---
|
||||
> [!TIP]
|
||||
> 當遇到未定義的 UI 區塊時,優先參考 `admin.dashboard.blade.php` 的卡片與即時動態實作方式進行衍生。
|
||||
@@ -112,8 +112,8 @@
|
||||
|
||||
.luxury-nav-item.active::before {
|
||||
content: "";
|
||||
@apply absolute left-0 w-1 h-5 bg-cyan-500 rounded-full shadow-[0_0_8px_rgba(6,182,212,0.5)];
|
||||
left: 0;
|
||||
@apply absolute left-0 w-1 h-5 bg-cyan-500 rounded-full;
|
||||
box-shadow: 0 0 8px rgba(6, 182, 212, 0.5);
|
||||
}
|
||||
|
||||
/* Submenu styling */
|
||||
@@ -121,4 +121,38 @@
|
||||
@apply mt-2 space-y-1 ps-4 border-l border-slate-200 ms-4;
|
||||
@apply dark:border-white/10;
|
||||
}
|
||||
|
||||
/* Luxury Buttons */
|
||||
.btn-luxury-primary {
|
||||
@apply inline-flex items-center justify-center gap-x-2 px-4 py-2.5 text-sm font-semibold rounded-xl text-white transition-all duration-200;
|
||||
background: linear-gradient(135deg, #06b6d4, #3b82f6);
|
||||
box-shadow: 0 4px 12px -2px rgba(6, 182, 212, 0.3);
|
||||
}
|
||||
|
||||
.btn-luxury-primary:hover {
|
||||
@apply -translate-y-0.5;
|
||||
box-shadow: 0 8px 20px -4px rgba(6, 182, 212, 0.4);
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
.btn-luxury-primary:active {
|
||||
@apply translate-y-0 scale-[0.98];
|
||||
}
|
||||
|
||||
.btn-luxury-secondary {
|
||||
@apply inline-flex items-center justify-center gap-x-2 px-3 py-2 text-sm font-semibold rounded-xl transition-all duration-200;
|
||||
@apply bg-white text-slate-700 border border-slate-200 shadow-sm;
|
||||
@apply dark:bg-slate-800 dark:text-white dark:border-slate-700;
|
||||
}
|
||||
|
||||
.btn-luxury-secondary:hover {
|
||||
@apply bg-slate-50 border-slate-300 -translate-y-0.5 shadow-md;
|
||||
@apply dark:bg-slate-700/50 dark:border-slate-600;
|
||||
}
|
||||
|
||||
.btn-luxury-ghost {
|
||||
@apply inline-flex items-center justify-center gap-x-2 px-4 py-2 text-sm font-bold rounded-xl transition-all duration-200;
|
||||
@apply text-slate-500 hover:bg-slate-100 hover:text-slate-900;
|
||||
@apply dark:text-slate-300 dark:hover:bg-white/10 dark:hover:text-cyan-400;
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@
|
||||
</div>
|
||||
|
||||
<div x-data="{ open: false, selected: '最近 7 天' }" class="relative inline-block text-left">
|
||||
<button @click="open = !open" type="button" class="inline-flex items-center gap-x-2 px-3 py-2 text-sm font-semibold text-slate-700 dark:text-slate-300 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-xl hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-all shadow-sm">
|
||||
<button @click="open = !open" type="button" class="btn-luxury-secondary">
|
||||
<span x-text="selected"></span>
|
||||
<svg class="shrink-0 w-4 h-4 transition-transform" :class="open ? 'rotate-180' : ''" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
|
||||
</button>
|
||||
@@ -113,7 +113,7 @@
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
<button class="w-full mt-8 py-3 text-sm font-bold text-slate-500 hover:text-cyan-500 transition-colors uppercase">查看所有日誌 →</button>
|
||||
<button class="btn-luxury-ghost w-full mt-8 py-3 font-bold uppercase tracking-wider">查看所有日誌 →</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<div class="container mx-auto px-6 py-8">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-gray-900 dark:text-gray-200 text-3xl font-medium">禮品設定</h3>
|
||||
<button onclick="document.getElementById('createModal').classList.remove('hidden')" class="bg-indigo-600 text-white px-4 py-2 rounded-md hover:bg-indigo-700">
|
||||
<button onclick="document.getElementById('createModal').classList.remove('hidden')" class="btn-luxury-primary">
|
||||
新增禮品
|
||||
</button>
|
||||
</div>
|
||||
@@ -148,8 +148,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end space-x-3">
|
||||
<button type="button" onclick="document.getElementById('createModal').classList.add('hidden')" class="px-4 py-2 text-gray-600 dark:text-gray-400 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">取消</button>
|
||||
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">建立</button>
|
||||
<button type="button" onclick="document.getElementById('createModal').classList.add('hidden')" class="btn-luxury-secondary">取消</button>
|
||||
<button type="submit" class="btn-luxury-primary">建立</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<div class="flex justify-end">
|
||||
<a href="{{ route('admin.machines.index') }}" class="bg-gray-200 dark:bg-gray-600 hover:bg-gray-300 dark:hover:bg-gray-500 text-gray-800 dark:text-white font-bold py-2 px-4 rounded mr-2">取消</a>
|
||||
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded">建立</button>
|
||||
<button type="submit" class="btn-luxury-primary">建立</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
<div class="flex justify-end">
|
||||
<a href="{{ route('admin.machines.index') }}" class="bg-gray-200 dark:bg-gray-600 hover:bg-gray-300 dark:hover:bg-gray-500 text-gray-800 dark:text-white font-bold py-2 px-4 rounded mr-2">取消</a>
|
||||
<button type="submit" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded">更新</button>
|
||||
<button type="submit" class="btn-luxury-primary">更新</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<div class="container mx-auto px-6 py-8">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-gray-900 dark:text-gray-200 text-3xl font-medium">會員等級設定</h3>
|
||||
<button onclick="document.getElementById('createModal').classList.remove('hidden')" class="bg-indigo-600 text-white px-4 py-2 rounded-md hover:bg-indigo-700">
|
||||
<button onclick="document.getElementById('createModal').classList.remove('hidden')" class="btn-luxury-primary">
|
||||
新增等級
|
||||
</button>
|
||||
</div>
|
||||
@@ -110,8 +110,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end space-x-3">
|
||||
<button type="button" onclick="document.getElementById('createModal').classList.add('hidden')" class="px-4 py-2 text-gray-600 dark:text-gray-400 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">取消</button>
|
||||
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">建立</button>
|
||||
<button type="button" onclick="document.getElementById('createModal').classList.add('hidden')" class="btn-luxury-secondary">取消</button>
|
||||
<button type="submit" class="btn-luxury-primary">建立</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<div class="container mx-auto px-6 py-8">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h3 class="text-gray-900 dark:text-gray-200 text-3xl font-medium">點數規則設定</h3>
|
||||
<button onclick="document.getElementById('createModal').classList.remove('hidden')" class="bg-indigo-600 text-white px-4 py-2 rounded-md hover:bg-indigo-700">
|
||||
<button onclick="document.getElementById('createModal').classList.remove('hidden')" class="btn-luxury-primary">
|
||||
新增規則
|
||||
</button>
|
||||
</div>
|
||||
@@ -128,8 +128,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 flex justify-end space-x-3">
|
||||
<button type="button" onclick="document.getElementById('createModal').classList.add('hidden')" class="px-4 py-2 text-gray-600 dark:text-gray-400 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-100 dark:hover:bg-gray-700">取消</button>
|
||||
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">建立</button>
|
||||
<button type="button" onclick="document.getElementById('createModal').classList.add('hidden')" class="btn-luxury-secondary">取消</button>
|
||||
<button type="submit" class="btn-luxury-primary">建立</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -41,8 +41,10 @@
|
||||
<!-- ========== HEADER ========== -->
|
||||
<header class="sticky top-0 inset-x-0 flex flex-wrap sm:justify-start sm:flex-nowrap z-[48] w-full bg-white/80 backdrop-blur-md border-b border-slate-200/50 text-sm py-2.5 sm:py-4 lg:pl-64 dark:bg-[#0f172a]/80 dark:border-slate-800/50 shadow-sm">
|
||||
<nav class="flex basis-full items-center w-full mx-auto px-4 sm:px-6 md:px-8" aria-label="Global">
|
||||
<div class="mr-5 lg:mr-0 lg:hidden">
|
||||
<a class="flex-none text-xl font-semibold dark:text-white" href="#" aria-label="Brand">Star Cloud</a>
|
||||
<div class="mr-5 lg:mr-0 lg:hidden text-center">
|
||||
<a class="flex-none text-xl font-bold dark:text-white font-display tracking-tight" href="{{ route('admin.dashboard') }}" aria-label="Brand">
|
||||
Star<span class="text-cyan-500">Cloud</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="w-full flex items-center justify-end ml-auto sm:justify-between sm:gap-x-3 sm:order-3">
|
||||
@@ -93,9 +95,9 @@
|
||||
<p class="text-sm font-medium text-gray-800 dark:text-gray-300">{{ Auth::user()->email }}</p>
|
||||
</div>
|
||||
<div class="mt-2 py-2 first:pt-0 last:pb-0">
|
||||
<a class="flex items-center gap-x-3.5 py-2 px-3 rounded-md text-sm text-gray-800 hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300" href="{{ route('profile.edit') }}">
|
||||
<a class="flex items-center gap-x-3.5 py-2 px-3 rounded-md text-sm text-gray-800 hover:bg-gray-100 focus:ring-2 focus:ring-blue-500 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-300 transition-colors" href="{{ route('profile.edit') }}">
|
||||
<svg class="flex-shrink-0 w-4 h-4" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
個人檔案
|
||||
帳戶設定
|
||||
</a>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
@@ -135,7 +137,7 @@
|
||||
</svg>
|
||||
</li>
|
||||
<li class="text-sm font-semibold text-gray-800 truncate dark:text-gray-200" aria-current="page">
|
||||
Dashboard
|
||||
儀表板
|
||||
</li>
|
||||
</ol>
|
||||
<!-- End Breadcrumb -->
|
||||
@@ -155,9 +157,12 @@
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="px-6 mb-4">
|
||||
<a class="flex-none text-2xl font-bold text-slate-900 dark:text-white font-display tracking-tight" href="#" aria-label="Brand">
|
||||
Star<span class="text-cyan-500">Cloud</span>
|
||||
<div class="px-6 mb-8 mt-2">
|
||||
<a class="flex items-center gap-x-2 text-2xl font-bold text-slate-900 dark:text-white font-display tracking-tight whitespace-nowrap" href="{{ route('admin.dashboard') }}" aria-label="Brand">
|
||||
<div class="w-8 h-8 rounded-lg bg-luxury-gradient flex items-center justify-center shadow-lg shadow-cyan-500/20">
|
||||
<span class="text-white text-base">S</span>
|
||||
</div>
|
||||
<span>Star<span class="text-cyan-500">Cloud</span></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user