All checks were successful
star-cloud-deploy-demo / deploy-demo (push) Successful in 1m4s
1. 將 B005 (廣告同步) 從 POST 改為 GET,符合 RESTful 規範。
2. 完善 B009 (庫存回報) 回應規格,加入業務代碼 (200 OK)。
3. API 文件 UI 優化:新增 Method Badge (方法標籤),並修正 JSON 中文/斜線轉義問題。
4. 機台管理介面優化:實作「唯讀庫存與效期」面板,並將日誌圖示改為「👁️」。
5. 標準化 ID 識別邏輯:資料表全面移除對 sku 的依賴,改以 id 為主、barcode 為輔。
6. 新增 Migration:正式移除 sku 欄位並同步 barcode 指向。
7. 更新多語系支援 (zh_TW, en, ja)。
362 lines
17 KiB
PHP
362 lines
17 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
|
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{{ $docs['title'] ?? 'API Documentation' }}</title>
|
|
|
|
<!-- Fonts -->
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap"
|
|
rel="stylesheet">
|
|
|
|
<!-- CSS (Vite or CDN for layout) -->
|
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
|
|
|
<style>
|
|
:root {
|
|
--color-luxury-deep: #f8fafc;
|
|
--color-luxury-card: #ffffff;
|
|
--color-accent: #0891b2;
|
|
/* Cyan-600 for light mode */
|
|
}
|
|
|
|
body {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
background-color: var(--color-luxury-deep);
|
|
color: #1e293b;
|
|
}
|
|
|
|
.font-display {
|
|
font-family: 'Outfit', sans-serif;
|
|
}
|
|
|
|
.sidebar {
|
|
width: 280px;
|
|
height: 100vh;
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
background: #ffffff;
|
|
border-right: 1px solid #e2e8f0;
|
|
z-index: 50;
|
|
}
|
|
|
|
.main-content {
|
|
margin-left: 280px;
|
|
padding: 2.5rem;
|
|
max-width: 1400px;
|
|
}
|
|
|
|
.method-badge {
|
|
padding: 4px 10px;
|
|
border-radius: 6px;
|
|
font-weight: 800;
|
|
font-size: 13px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.method-post {
|
|
background: #0891b2;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.method-get {
|
|
background: #10b981;
|
|
color: #ffffff;
|
|
}
|
|
|
|
.param-table th {
|
|
text-align: left;
|
|
padding: 0.75rem 1rem;
|
|
font-size: 14px;
|
|
font-weight: 800;
|
|
color: #64748b;
|
|
background: #f1f5f9;
|
|
border-bottom: 2px solid #e2e8f0;
|
|
}
|
|
|
|
.param-table td {
|
|
padding: 0.75rem 1rem;
|
|
border-bottom: 1px solid #f1f5f9;
|
|
font-size: 15px;
|
|
}
|
|
|
|
pre {
|
|
background: #1e293b !important;
|
|
color: #e2e8f0 !important;
|
|
border-radius: 12px;
|
|
padding: 1.25rem;
|
|
font-family: 'ui-monospace', monospace;
|
|
font-size: 14px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.luxury-nav-section {
|
|
font-size: 12px;
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
color: #94a3b8;
|
|
margin-top: 1.5rem;
|
|
margin-bottom: 0.5rem;
|
|
padding-left: 1.5rem;
|
|
}
|
|
|
|
.luxury-nav-link {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 0.6rem 1.5rem;
|
|
color: #475569;
|
|
font-size: 15px;
|
|
font-weight: 700;
|
|
transition: all 0.2s;
|
|
border-left: 4px solid transparent;
|
|
}
|
|
|
|
.luxury-nav-link:hover {
|
|
background: #f8fafc;
|
|
color: #0f172a;
|
|
}
|
|
|
|
.luxury-nav-link.active {
|
|
color: var(--color-accent);
|
|
background: #ecfeff;
|
|
border-left-color: var(--color-accent);
|
|
}
|
|
|
|
.animate-luxury-in {
|
|
animation: luxuryIn 0.5s ease-out forwards;
|
|
}
|
|
|
|
@keyframes luxuryIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(10px);
|
|
}
|
|
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body x-data="{ activeSection: '{{ $docs['categories'][0]['apis'][0]['slug'] ?? '' }}' }">
|
|
|
|
<!-- Sidebar -->
|
|
<div class="sidebar shadow-sm">
|
|
<div class="p-6 border-b border-slate-100">
|
|
<h1 class="font-display text-2xl font-black tracking-tighter text-slate-900">
|
|
STAR CLOUD <span class="text-cyan-600">文件</span>
|
|
</h1>
|
|
<p class="text-[11px] font-black text-slate-400 uppercase tracking-widest mt-1">API 參考手冊 {{ $docs['version']
|
|
}}</p>
|
|
</div>
|
|
|
|
<nav class="mt-2 overflow-y-auto" style="height: calc(100vh - 100px);">
|
|
@foreach($docs['categories'] as $category)
|
|
<div class="luxury-nav-section">{{ $category['name'] }}</div>
|
|
@foreach($category['apis'] as $api)
|
|
<a href="#{{ $api['slug'] }}" class="luxury-nav-link"
|
|
:class="{ 'active': activeSection === '{{ $api['slug'] }}' }"
|
|
@click="activeSection = '{{ $api['slug'] }}'">
|
|
<div class="flex items-center gap-2 overflow-hidden w-full">
|
|
<span class="flex-shrink-0 text-[10px] w-10 text-center font-black px-1.5 py-0.5 rounded-md uppercase tracking-tighter {{ $api['method'] === 'GET' ? 'bg-emerald-50 text-emerald-600 border border-emerald-200' : ($api['method'] === 'POST' ? 'bg-cyan-50 text-cyan-600 border border-cyan-200' : 'bg-amber-50 text-amber-600 border border-amber-200') }}">
|
|
{{ $api['method'] }}
|
|
</span>
|
|
<span class="truncate">{{ $api['name'] }}</span>
|
|
</div>
|
|
</a>
|
|
@endforeach
|
|
@endforeach
|
|
</nav>
|
|
</div>
|
|
|
|
<!-- Main Content -->
|
|
<div class="main-content">
|
|
<div class="mb-10 p-8 bg-white rounded-3xl border border-slate-100 shadow-sm">
|
|
<h2 class="font-display text-4xl font-black tracking-tight text-slate-900 mb-4">{{ $docs['title'] }}</h2>
|
|
<p class="text-slate-500 leading-relaxed text-lg max-w-3xl font-medium">
|
|
{{ $docs['description'] }}
|
|
</p>
|
|
</div>
|
|
|
|
@foreach($docs['categories'] as $category)
|
|
@foreach($category['apis'] as $api)
|
|
<div id="{{ $api['slug'] }}" class="mb-16 animate-luxury-in" x-intersect="activeSection = '{{ $api['slug'] }}'">
|
|
<div class="mb-4"></div>
|
|
|
|
<div class="flex items-center gap-4 mb-6">
|
|
<span class="px-3 py-1 text-sm font-black rounded-lg uppercase tracking-widest {{ $api['method'] === 'GET' ? 'bg-emerald-100 text-emerald-700' : ($api['method'] === 'POST' ? 'bg-cyan-100 text-cyan-700' : 'bg-amber-100 text-amber-700') }}">
|
|
{{ $api['method'] }}
|
|
</span>
|
|
<h3 class="font-display text-3xl font-black text-slate-900 tracking-tight">{{ $api['name'] }}</h3>
|
|
</div>
|
|
<p class="text-slate-600 mb-8 text-lg font-medium">{{ $api['description'] }}</p>
|
|
|
|
<!-- Headers & URL -->
|
|
<div class="mb-6 overflow-hidden rounded-2xl border border-slate-200">
|
|
<div class="bg-slate-900 px-6 py-4 flex items-center justify-between">
|
|
<div class="flex items-center gap-3">
|
|
<span
|
|
class="px-3 py-1 bg-cyan-500 text-white text-xs font-black rounded-lg uppercase letter-spacing-widest">{{
|
|
$api['method'] }}</span>
|
|
<code
|
|
class="text-slate-300 font-mono text-base">{{ config('app.url') }}{{ $api['path'] }}</code>
|
|
</div>
|
|
<span
|
|
class="text-slate-500 text-[10px] font-black uppercase tracking-widest hidden sm:block">Endpoint
|
|
URL</span>
|
|
</div>
|
|
<div class="bg-slate-50 p-6 border-t border-slate-200">
|
|
<h4
|
|
class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4 flex items-center gap-2">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 00-2 2zm10-10V7a4 4 0 00-8 0v4h8z" />
|
|
</svg>
|
|
請求標頭 (Headers)
|
|
</h4>
|
|
<div class="space-y-4">
|
|
@foreach($api['headers'] as $key => $val)
|
|
<div class="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4">
|
|
<span class="text-slate-500 font-bold min-w-[140px] text-sm font-mono tracking-tight">{{
|
|
$key }}</span>
|
|
<div class="flex-1">
|
|
<code
|
|
class="text-cyan-700 font-bold bg-white px-3 py-1.5 rounded-lg border border-cyan-100 text-sm italic shadow-sm break-all block sm:inline-block w-full sm:w-auto">
|
|
{{ $val }}
|
|
</code>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Parameters -->
|
|
<div class="mb-8">
|
|
<h4 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4">請求主體 (Parameters)</h4>
|
|
<div class="overflow-hidden rounded-2xl border border-slate-200 shadow-sm bg-white">
|
|
<table class="w-full param-table">
|
|
<thead>
|
|
<tr>
|
|
<th class="w-[20%]">欄位名稱</th>
|
|
<th class="w-[15%]">型態</th>
|
|
<th>說明</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($api['parameters'] as $name => $param)
|
|
<tr class="hover:bg-slate-50/50 transition-colors">
|
|
<td class="font-black text-slate-900 text-base">
|
|
{{ $name }}
|
|
@if($param['required'] ?? false)
|
|
<span class="text-rose-500 ml-1">*</span>
|
|
@endif
|
|
</td>
|
|
<td>
|
|
<span
|
|
class="px-2 py-1 rounded bg-slate-100 text-xs font-black text-slate-600 uppercase tracking-wider">
|
|
{{ $param['type'] }}
|
|
</span>
|
|
</td>
|
|
<td class="text-slate-600 leading-relaxed font-semibold">
|
|
{{ $param['description'] }}
|
|
@if(isset($param['example']))
|
|
<div class="mt-2 flex items-center gap-2">
|
|
<span
|
|
class="text-[11px] font-black text-slate-400 uppercase tracking-tight">範例:</span>
|
|
<code
|
|
class="text-sm text-cyan-600 font-bold">{{ is_array($param['example']) ? json_encode($param['example'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $param['example'] }}</code>
|
|
</div>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Request & Response Examples -->
|
|
<div class="space-y-12 mt-12">
|
|
<!-- Request Example -->
|
|
<div>
|
|
<h4 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-6">請求範例 (Request Body)</h4>
|
|
<pre><code>{{ json_encode($api['request'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) }}</code></pre>
|
|
</div>
|
|
|
|
<!-- Response Examples -->
|
|
<div>
|
|
@if(isset($api['response_parameters']))
|
|
<h4 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4">回應參數說明 (Response Parameters)</h4>
|
|
<div class="overflow-hidden rounded-2xl border border-slate-200 shadow-sm bg-white mb-8">
|
|
<table class="w-full param-table">
|
|
<thead>
|
|
<tr>
|
|
<th class="w-[20%]">欄位名稱</th>
|
|
<th class="w-[15%]">型態</th>
|
|
<th>說明</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach($api['response_parameters'] as $name => $param)
|
|
<tr class="hover:bg-slate-50/50 transition-colors">
|
|
<td class="font-black text-slate-900 text-sm italic">{{ $name }}</td>
|
|
<td>
|
|
<span class="px-2 py-1 rounded bg-slate-100 text-[10px] font-black text-slate-600 uppercase tracking-wider">
|
|
{{ $param['type'] }}
|
|
</span>
|
|
</td>
|
|
<td class="text-slate-600 font-semibold text-sm whitespace-pre-line">
|
|
{{ $param['description'] }}
|
|
@if(isset($param['example']))
|
|
<div class="mt-2 flex items-center gap-2">
|
|
<span class="text-[10px] font-black text-slate-400 uppercase tracking-tight">範例:</span>
|
|
<code class="text-xs text-cyan-600 font-bold bg-cyan-50/50 px-2 py-0.5 rounded">{{ is_array($param['example']) ? json_encode($param['example'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : $param['example'] }}</code>
|
|
</div>
|
|
@endif
|
|
</td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
@endif
|
|
|
|
<h4 class="text-xs font-black text-slate-400 uppercase tracking-widest mb-4">回應範例 (Response Body)</h4>
|
|
<pre><code>{{ json_encode($api['response'], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) }}</code></pre>
|
|
</div>
|
|
|
|
@if(isset($api['notes']))
|
|
<div class="p-8 bg-cyan-50/50 border border-cyan-100 rounded-3xl">
|
|
<h5 class="text-cyan-700 font-black mb-4 flex items-center gap-2 text-xl">
|
|
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
|
實作備註
|
|
</h5>
|
|
<p class="text-cyan-800 font-semibold leading-relaxed text-lg whitespace-pre-line">
|
|
{{ $api['notes'] }}
|
|
</p>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
@endforeach
|
|
</div>
|
|
|
|
<!-- Font Awesome or Lucide (Simplified) -->
|
|
<script src="https://unpkg.com/lucide@latest"></script>
|
|
<script>
|
|
lucide.createIcons();
|
|
</script>
|
|
</body>
|
|
|
|
</html> |