Show badge only in local test environment

This commit is contained in:
2026-04-15 16:32:53 +08:00
parent c343dc6f11
commit 78142488f3
3 changed files with 52 additions and 0 deletions

View File

@@ -49,6 +49,9 @@ LINE 推播目標支援分成兩組:
- `LINE_TARGET_ID_PROD`: 正式環境用對話
- `LINE_TARGET_MODE`: `local``prod`
當系統偵測目前為 `local` 模式時,頁面右上角會顯示「測試環境」標籤。
正式環境 `prod` 不會顯示這個標籤。
## 資料庫欄位
- `time`: 目標日期,格式為 `YYYYMMDD`

View File

@@ -29,10 +29,27 @@
}
.hero-copy {
position: relative;
padding: 36px;
background: linear-gradient(145deg, rgba(255, 247, 234, 0.92), rgba(232, 244, 235, 0.84));
}
.env-badge {
position: absolute;
top: 20px;
right: 20px;
display: inline-flex;
align-items: center;
min-height: 34px;
padding: 0 14px;
border-radius: 999px;
background: rgba(186, 61, 47, 0.12);
color: #8d2d22;
font-size: 0.84rem;
font-weight: 700;
letter-spacing: 0.08em;
}
.eyebrow {
margin: 0 0 12px;
color: #1d6c46;
@@ -371,6 +388,11 @@
padding: 18px;
}
.env-badge {
position: static;
margin-bottom: 14px;
}
.team-line {
flex-wrap: wrap;
}

View File

@@ -26,6 +26,10 @@ type LoadMatchResultsResponse = {
battlecombination: string | null
}
type HealthResponse = {
lineTargetMode?: string
}
const STORAGE_KEYS = {
areaA: 'badminton-match-hub::area-a',
areaB: 'badminton-match-hub::area-b',
@@ -49,6 +53,7 @@ function App() {
const [error, setError] = useState('')
const [actionState, setActionState] = useState<ActionState>('idle')
const [actionMessage, setActionMessage] = useState('')
const [showTestBadge, setShowTestBadge] = useState(false)
useEffect(() => {
window.localStorage.setItem(STORAGE_KEYS.areaA, areaAInput)
@@ -58,6 +63,13 @@ function App() {
window.localStorage.setItem(STORAGE_KEYS.areaB, areaBInput)
}, [areaBInput])
useEffect(() => {
void (async () => {
const isLocalMode = await loadEnvironmentMode()
setShowTestBadge(isLocalMode)
})()
}, [])
const parsedAreaA = parseRoster(areaAInput)
const parsedAreaB = parseRoster(areaBInput)
const teamCount = Math.max(parsedAreaA.length, parsedAreaB.length)
@@ -196,6 +208,7 @@ function App() {
<main className="app-shell">
<section className="hero-card">
<div className="hero-copy">
{showTestBadge ? <div className="env-badge"></div> : null}
<p className="eyebrow"></p>
<h1></h1>
<p className="hero-text">
@@ -477,6 +490,20 @@ async function pushMatchResultsToLine(time: string, rounds: RoundResult[]) {
}
}
async function loadEnvironmentMode() {
try {
const response = await fetch('/api/health')
if (!response.ok) {
return false
}
const payload = (await response.json()) as HealthResponse
return payload.lineTargetMode === 'local'
} catch {
return false
}
}
function convertDbRecordToAppState(record: LoadMatchResultsResponse) {
const personnel = JSON.parse(record.personnel) as [number, string][]
const battlecombination = JSON.parse(record.battlecombination ?? '{}') as Record<