修正記分板在 iPad 尺寸的版面與修復 dev server 啟動問題

This commit is contained in:
2026-05-18 17:40:58 +08:00
parent 30a8e1a44c
commit 3677162747
7 changed files with 4599 additions and 4598 deletions

1
.gitignore vendored
View File

@@ -25,3 +25,4 @@ dist-ssr
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
/*.stackdump

View File

@@ -6,7 +6,7 @@
"scripts": { "scripts": {
"dev": "concurrently \"npm:dev:server\" \"npm:dev:client\"", "dev": "concurrently \"npm:dev:server\" \"npm:dev:client\"",
"dev:client": "vite", "dev:client": "vite",
"dev:server": "node --watch server/server.mjs", "dev:server": "node server/server.mjs",
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview", "preview": "vite preview",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,38 +1,38 @@
:root { :root {
--page-bg: #eff5df; --page-bg: #eff5df;
--page-bg-2: #dbe8c6; --page-bg-2: #dbe8c6;
--panel-strong: #0a332d; --panel-strong: #0a332d;
--panel-soft: #587169; --panel-soft: #587169;
--border: rgba(7, 51, 44, 0.12); --border: rgba(7, 51, 44, 0.12);
--shadow: --shadow:
0 24px 60px rgba(19, 43, 34, 0.12), 0 24px 60px rgba(19, 43, 34, 0.12),
0 8px 20px rgba(19, 43, 34, 0.08); 0 8px 20px rgba(19, 43, 34, 0.08);
--sans: 'Bahnschrift', 'Trebuchet MS', sans-serif; --sans: 'Bahnschrift', 'Trebuchet MS', sans-serif;
--heading: 'Arial Black', 'Bahnschrift', sans-serif; --heading: 'Arial Black', 'Bahnschrift', sans-serif;
--mono: 'Consolas', 'Courier New', monospace; --mono: 'Consolas', 'Courier New', monospace;
font: 18px/1.5 var(--sans); font: 18px/1.5 var(--sans);
color: var(--panel-strong); color: var(--panel-strong);
font-synthesis: none; font-synthesis: none;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
* { * {
box-sizing: border-box; box-sizing: border-box;
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
} }
html { html {
min-height: 100%; min-height: 100%;
} }
body { body {
margin: 0; margin: 0;
min-height: 100vh; min-height: 100vh;
background: background:
radial-gradient(circle at top left, rgba(255, 214, 10, 0.35), transparent 28%), radial-gradient(circle at top left, rgba(255, 214, 10, 0.35), transparent 28%),
radial-gradient(circle at bottom right, rgba(11, 88, 73, 0.2), transparent 32%), radial-gradient(circle at bottom right, rgba(11, 88, 73, 0.2), transparent 32%),
linear-gradient(180deg, var(--page-bg), var(--page-bg-2)); linear-gradient(180deg, var(--page-bg), var(--page-bg-2));
} }
@@ -43,17 +43,17 @@ body.body-scoreboard {
} }
body::before { body::before {
content: ''; content: '';
position: fixed; position: fixed;
inset: 0; inset: 0;
pointer-events: none; pointer-events: none;
background-image: background-image:
linear-gradient(rgba(255, 255, 255, 0.14) 1px, transparent 1px), linear-gradient(rgba(255, 255, 255, 0.14) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.14) 1px, transparent 1px); linear-gradient(90deg, rgba(255, 255, 255, 0.14) 1px, transparent 1px);
background-size: 48px 48px; background-size: 48px 48px;
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.45), transparent 78%); mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.45), transparent 78%);
} }
#root { #root {
min-height: 100vh; min-height: 100vh;
} }
@@ -62,46 +62,46 @@ body.body-scoreboard #root {
min-height: 100dvh; min-height: 100dvh;
overflow: hidden; overflow: hidden;
} }
input, input,
textarea, textarea,
select, select,
button, button,
[contenteditable='true'] { [contenteditable='true'] {
-webkit-user-select: auto; -webkit-user-select: auto;
user-select: auto; user-select: auto;
} }
h1, h1,
h2, h2,
h3, h3,
p { p {
margin: 0; margin: 0;
} }
h1, h1,
h2, h2,
h3 { h3 {
font-family: var(--heading); font-family: var(--heading);
letter-spacing: 0.02em; letter-spacing: 0.02em;
} }
h1 { h1 {
margin: 18px 0 14px; margin: 18px 0 14px;
font-size: clamp(2.8rem, 8vw, 5rem); font-size: clamp(2.8rem, 8vw, 5rem);
line-height: 0.94; line-height: 0.94;
} }
h2 { h2 {
font-size: clamp(1.6rem, 3vw, 2.2rem); font-size: clamp(1.6rem, 3vw, 2.2rem);
} }
p { p {
color: var(--panel-soft); color: var(--panel-soft);
} }
@media (max-width: 720px) { @media (max-width: 720px) {
:root { :root {
font-size: 16px; font-size: 16px;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,40 @@
export type LoadStatus = 'idle' | 'loading' | 'loaded' | 'empty' | 'error' export type LoadStatus = 'idle' | 'loading' | 'loaded' | 'empty' | 'error'
export type ScoreSide = 'left' | 'right' export type ScoreSide = 'left' | 'right'
export type PlayerSlot = 'playerA' | 'playerB' export type PlayerSlot = 'playerA' | 'playerB'
export type CourtSide = 'left' | 'right' export type CourtSide = 'left' | 'right'
export type GroupTeam = { export type GroupTeam = {
id: number id: number
playerA: string playerA: string
playerB: string playerB: string
isPlaceholderA: boolean isPlaceholderA: boolean
isPlaceholderB: boolean isPlaceholderB: boolean
} }
export type RoundGroup = { export type RoundGroup = {
id: number id: number
teams: GroupTeam[] teams: GroupTeam[]
} }
export type MatchResultsRecord = { export type MatchResultsRecord = {
time: number time: number
personnel: string personnel: string
battlecombination: string | null battlecombination: string | null
} }
export type Matchup = { export type Matchup = {
leftTeamId: number | null leftTeamId: number | null
rightTeamId: number | null rightTeamId: number | null
} }
export type ActiveMatchup = { export type ActiveMatchup = {
leftTeam: GroupTeam | null leftTeam: GroupTeam | null
rightTeam: GroupTeam | null rightTeam: GroupTeam | null
} }
export type ScoreState = { export type ScoreState = {
scoreLeft: number scoreLeft: number
scoreRight: number scoreRight: number
@@ -47,117 +47,117 @@ export type ScoreState = {
leftRightCourtPlayer: PlayerSlot leftRightCourtPlayer: PlayerSlot
rightRightCourtPlayer: PlayerSlot rightRightCourtPlayer: PlayerSlot
} }
export type PointHistoryEntry = { export type PointHistoryEntry = {
round: number round: number
starter: number starter: number
winCount: number winCount: number
winner: 0 | 1 winner: 0 | 1
} }
export type ScoreSnapshot = { export type ScoreSnapshot = {
pointLog: PointHistoryEntry[] pointLog: PointHistoryEntry[]
scoreState: ScoreState scoreState: ScoreState
} }
export type MatchHistoryItem = { export type MatchHistoryItem = {
id: string id: string
playedAt: string playedAt: string
matchDate: string matchDate: string
source: 'db' | 'manual' | 'idle' source: 'db' | 'manual' | 'idle'
groupId: number groupId: number
leftTeamName: string leftTeamName: string
rightTeamName: string rightTeamName: string
scoreLeft: number scoreLeft: number
scoreRight: number scoreRight: number
winner: string winner: string
} }
export type HistoryUploadPayload = { export type HistoryUploadPayload = {
dayOfWeek: number dayOfWeek: number
players: string[] players: string[]
score: [number, number] score: [number, number]
scoreList: Array<[number, number, number, 0 | 1]> scoreList: Array<[number, number, number, 0 | 1]>
team: [string[], string[]] team: [string[], string[]]
time: number time: number
type: 0 | 1 type: 0 | 1
winScore: number winScore: number
} }
export type HistoryUploadResponse = { export type HistoryUploadResponse = {
id: number id: number
} }
export type HistoryRecord = { export type HistoryRecord = {
id: number id: number
time: number time: number
dayOfWeek: number dayOfWeek: number
score: string score: string
winScore: number winScore: number
type: 0 | 1 type: 0 | 1
players: string players: string
team: string team: string
scoreList: string | null scoreList: string | null
} }
export type HistoryListItem = { export type HistoryListItem = {
id: number id: number
time: number time: number
playedAt: string playedAt: string
dayOfWeek: number dayOfWeek: number
dayLabel: string dayLabel: string
score: [number, number] score: [number, number]
winScore: number winScore: number
type: 0 | 1 type: 0 | 1
typeLabel: string typeLabel: string
players: string[] players: string[]
team: [string[], string[]] team: [string[], string[]]
scoreList: Array<[number, number, number, 0 | 1]> scoreList: Array<[number, number, number, 0 | 1]>
leftTeamName: string leftTeamName: string
rightTeamName: string rightTeamName: string
winnerTeamName: string winnerTeamName: string
} }
export type LiveRoomStatus = 'live' | 'finished' export type LiveRoomStatus = 'live' | 'finished'
export type LiveRoomSession = { export type LiveRoomSession = {
hostToken: string hostToken: string
roomId: string roomId: string
status: LiveRoomStatus status: LiveRoomStatus
} }
export type LiveRoomSummary = { export type LiveRoomSummary = {
roomId: string roomId: string
createdAt: string createdAt: string
leftTeamName: string leftTeamName: string
rightTeamName: string rightTeamName: string
scoreLeft: number scoreLeft: number
scoreRight: number scoreRight: number
status: LiveRoomStatus status: LiveRoomStatus
targetScore: number targetScore: number
updatedAt: string updatedAt: string
} }
export type LiveRoomPayload = { export type LiveRoomPayload = {
groupId: number | null groupId: number | null
leftTeamName: string leftTeamName: string
matchupLabel: string matchupLabel: string
pointLog: PointHistoryEntry[] pointLog: PointHistoryEntry[]
rightTeamName: string rightTeamName: string
scoreState: ScoreState scoreState: ScoreState
targetDate: string targetDate: string
} }
export type LiveRoomDetail = LiveRoomPayload & { export type LiveRoomDetail = LiveRoomPayload & {
createdAt: string createdAt: string
roomId: string roomId: string
status: LiveRoomStatus status: LiveRoomStatus
updatedAt: string updatedAt: string
winnerTeamName: string | null winnerTeamName: string | null
} }
export type LiveRoomUpdatePayload = LiveRoomPayload & { export type LiveRoomUpdatePayload = LiveRoomPayload & {
hostToken: string hostToken: string
status: LiveRoomStatus status: LiveRoomStatus
winnerTeamName: string | null winnerTeamName: string | null
} }