修正記分板在 iPad 尺寸的版面與修復 dev server 啟動問題
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,3 +25,4 @@ dist-ssr
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
/*.stackdump
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"dev": "concurrently \"npm:dev:server\" \"npm:dev:client\"",
|
||||
"dev:client": "vite",
|
||||
"dev:server": "node --watch server/server.mjs",
|
||||
"dev:server": "node server/server.mjs",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
|
||||
4706
src/App.css
4706
src/App.css
File diff suppressed because it is too large
Load Diff
1886
src/App.tsx
1886
src/App.tsx
File diff suppressed because it is too large
Load Diff
168
src/index.css
168
src/index.css
@@ -1,38 +1,38 @@
|
||||
:root {
|
||||
--page-bg: #eff5df;
|
||||
--page-bg-2: #dbe8c6;
|
||||
--panel-strong: #0a332d;
|
||||
--panel-soft: #587169;
|
||||
--border: rgba(7, 51, 44, 0.12);
|
||||
--shadow:
|
||||
0 24px 60px rgba(19, 43, 34, 0.12),
|
||||
0 8px 20px rgba(19, 43, 34, 0.08);
|
||||
--sans: 'Bahnschrift', 'Trebuchet MS', sans-serif;
|
||||
--heading: 'Arial Black', 'Bahnschrift', sans-serif;
|
||||
--mono: 'Consolas', 'Courier New', monospace;
|
||||
font: 18px/1.5 var(--sans);
|
||||
color: var(--panel-strong);
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
:root {
|
||||
--page-bg: #eff5df;
|
||||
--page-bg-2: #dbe8c6;
|
||||
--panel-strong: #0a332d;
|
||||
--panel-soft: #587169;
|
||||
--border: rgba(7, 51, 44, 0.12);
|
||||
--shadow:
|
||||
0 24px 60px rgba(19, 43, 34, 0.12),
|
||||
0 8px 20px rgba(19, 43, 34, 0.08);
|
||||
--sans: 'Bahnschrift', 'Trebuchet MS', sans-serif;
|
||||
--heading: 'Arial Black', 'Bahnschrift', sans-serif;
|
||||
--mono: 'Consolas', 'Courier New', monospace;
|
||||
font: 18px/1.5 var(--sans);
|
||||
color: var(--panel-strong);
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(255, 214, 10, 0.35), transparent 28%),
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
background:
|
||||
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%),
|
||||
linear-gradient(180deg, var(--page-bg), var(--page-bg-2));
|
||||
}
|
||||
@@ -43,17 +43,17 @@ body.body-scoreboard {
|
||||
}
|
||||
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background-image:
|
||||
linear-gradient(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;
|
||||
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.45), transparent 78%);
|
||||
}
|
||||
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background-image:
|
||||
linear-gradient(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;
|
||||
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.45), transparent 78%);
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
}
|
||||
@@ -62,46 +62,46 @@ body.body-scoreboard #root {
|
||||
min-height: 100dvh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
button,
|
||||
[contenteditable='true'] {
|
||||
-webkit-user-select: auto;
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: var(--heading);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 18px 0 14px;
|
||||
font-size: clamp(2.8rem, 8vw, 5rem);
|
||||
line-height: 0.94;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.6rem, 3vw, 2.2rem);
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--panel-soft);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
:root {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
input,
|
||||
textarea,
|
||||
select,
|
||||
button,
|
||||
[contenteditable='true'] {
|
||||
-webkit-user-select: auto;
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: var(--heading);
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 18px 0 14px;
|
||||
font-size: clamp(2.8rem, 8vw, 5rem);
|
||||
line-height: 0.94;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.6rem, 3vw, 2.2rem);
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--panel-soft);
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
:root {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
302
src/types.ts
302
src/types.ts
@@ -1,40 +1,40 @@
|
||||
export type LoadStatus = 'idle' | 'loading' | 'loaded' | 'empty' | 'error'
|
||||
|
||||
export type ScoreSide = 'left' | 'right'
|
||||
|
||||
export type PlayerSlot = 'playerA' | 'playerB'
|
||||
|
||||
export type CourtSide = 'left' | 'right'
|
||||
|
||||
export type GroupTeam = {
|
||||
id: number
|
||||
playerA: string
|
||||
playerB: string
|
||||
isPlaceholderA: boolean
|
||||
isPlaceholderB: boolean
|
||||
}
|
||||
|
||||
export type RoundGroup = {
|
||||
id: number
|
||||
teams: GroupTeam[]
|
||||
}
|
||||
|
||||
export type MatchResultsRecord = {
|
||||
time: number
|
||||
personnel: string
|
||||
battlecombination: string | null
|
||||
}
|
||||
|
||||
export type Matchup = {
|
||||
leftTeamId: number | null
|
||||
rightTeamId: number | null
|
||||
}
|
||||
|
||||
export type ActiveMatchup = {
|
||||
leftTeam: GroupTeam | null
|
||||
rightTeam: GroupTeam | null
|
||||
}
|
||||
|
||||
export type LoadStatus = 'idle' | 'loading' | 'loaded' | 'empty' | 'error'
|
||||
|
||||
export type ScoreSide = 'left' | 'right'
|
||||
|
||||
export type PlayerSlot = 'playerA' | 'playerB'
|
||||
|
||||
export type CourtSide = 'left' | 'right'
|
||||
|
||||
export type GroupTeam = {
|
||||
id: number
|
||||
playerA: string
|
||||
playerB: string
|
||||
isPlaceholderA: boolean
|
||||
isPlaceholderB: boolean
|
||||
}
|
||||
|
||||
export type RoundGroup = {
|
||||
id: number
|
||||
teams: GroupTeam[]
|
||||
}
|
||||
|
||||
export type MatchResultsRecord = {
|
||||
time: number
|
||||
personnel: string
|
||||
battlecombination: string | null
|
||||
}
|
||||
|
||||
export type Matchup = {
|
||||
leftTeamId: number | null
|
||||
rightTeamId: number | null
|
||||
}
|
||||
|
||||
export type ActiveMatchup = {
|
||||
leftTeam: GroupTeam | null
|
||||
rightTeam: GroupTeam | null
|
||||
}
|
||||
|
||||
export type ScoreState = {
|
||||
scoreLeft: number
|
||||
scoreRight: number
|
||||
@@ -47,117 +47,117 @@ export type ScoreState = {
|
||||
leftRightCourtPlayer: PlayerSlot
|
||||
rightRightCourtPlayer: PlayerSlot
|
||||
}
|
||||
|
||||
export type PointHistoryEntry = {
|
||||
round: number
|
||||
starter: number
|
||||
winCount: number
|
||||
winner: 0 | 1
|
||||
}
|
||||
|
||||
export type ScoreSnapshot = {
|
||||
pointLog: PointHistoryEntry[]
|
||||
scoreState: ScoreState
|
||||
}
|
||||
|
||||
export type MatchHistoryItem = {
|
||||
id: string
|
||||
playedAt: string
|
||||
matchDate: string
|
||||
source: 'db' | 'manual' | 'idle'
|
||||
groupId: number
|
||||
leftTeamName: string
|
||||
rightTeamName: string
|
||||
scoreLeft: number
|
||||
scoreRight: number
|
||||
winner: string
|
||||
}
|
||||
|
||||
export type HistoryUploadPayload = {
|
||||
dayOfWeek: number
|
||||
players: string[]
|
||||
score: [number, number]
|
||||
scoreList: Array<[number, number, number, 0 | 1]>
|
||||
team: [string[], string[]]
|
||||
time: number
|
||||
type: 0 | 1
|
||||
winScore: number
|
||||
}
|
||||
|
||||
export type HistoryUploadResponse = {
|
||||
id: number
|
||||
}
|
||||
|
||||
export type HistoryRecord = {
|
||||
id: number
|
||||
time: number
|
||||
dayOfWeek: number
|
||||
score: string
|
||||
winScore: number
|
||||
type: 0 | 1
|
||||
players: string
|
||||
team: string
|
||||
scoreList: string | null
|
||||
}
|
||||
|
||||
export type HistoryListItem = {
|
||||
id: number
|
||||
time: number
|
||||
playedAt: string
|
||||
dayOfWeek: number
|
||||
dayLabel: string
|
||||
score: [number, number]
|
||||
winScore: number
|
||||
type: 0 | 1
|
||||
typeLabel: string
|
||||
players: string[]
|
||||
team: [string[], string[]]
|
||||
scoreList: Array<[number, number, number, 0 | 1]>
|
||||
leftTeamName: string
|
||||
rightTeamName: string
|
||||
winnerTeamName: string
|
||||
}
|
||||
|
||||
export type LiveRoomStatus = 'live' | 'finished'
|
||||
|
||||
export type LiveRoomSession = {
|
||||
hostToken: string
|
||||
roomId: string
|
||||
status: LiveRoomStatus
|
||||
}
|
||||
|
||||
export type LiveRoomSummary = {
|
||||
roomId: string
|
||||
createdAt: string
|
||||
leftTeamName: string
|
||||
rightTeamName: string
|
||||
scoreLeft: number
|
||||
scoreRight: number
|
||||
status: LiveRoomStatus
|
||||
targetScore: number
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type LiveRoomPayload = {
|
||||
groupId: number | null
|
||||
leftTeamName: string
|
||||
matchupLabel: string
|
||||
pointLog: PointHistoryEntry[]
|
||||
rightTeamName: string
|
||||
scoreState: ScoreState
|
||||
targetDate: string
|
||||
}
|
||||
|
||||
export type LiveRoomDetail = LiveRoomPayload & {
|
||||
createdAt: string
|
||||
roomId: string
|
||||
status: LiveRoomStatus
|
||||
updatedAt: string
|
||||
winnerTeamName: string | null
|
||||
}
|
||||
|
||||
export type LiveRoomUpdatePayload = LiveRoomPayload & {
|
||||
hostToken: string
|
||||
status: LiveRoomStatus
|
||||
winnerTeamName: string | null
|
||||
}
|
||||
|
||||
export type PointHistoryEntry = {
|
||||
round: number
|
||||
starter: number
|
||||
winCount: number
|
||||
winner: 0 | 1
|
||||
}
|
||||
|
||||
export type ScoreSnapshot = {
|
||||
pointLog: PointHistoryEntry[]
|
||||
scoreState: ScoreState
|
||||
}
|
||||
|
||||
export type MatchHistoryItem = {
|
||||
id: string
|
||||
playedAt: string
|
||||
matchDate: string
|
||||
source: 'db' | 'manual' | 'idle'
|
||||
groupId: number
|
||||
leftTeamName: string
|
||||
rightTeamName: string
|
||||
scoreLeft: number
|
||||
scoreRight: number
|
||||
winner: string
|
||||
}
|
||||
|
||||
export type HistoryUploadPayload = {
|
||||
dayOfWeek: number
|
||||
players: string[]
|
||||
score: [number, number]
|
||||
scoreList: Array<[number, number, number, 0 | 1]>
|
||||
team: [string[], string[]]
|
||||
time: number
|
||||
type: 0 | 1
|
||||
winScore: number
|
||||
}
|
||||
|
||||
export type HistoryUploadResponse = {
|
||||
id: number
|
||||
}
|
||||
|
||||
export type HistoryRecord = {
|
||||
id: number
|
||||
time: number
|
||||
dayOfWeek: number
|
||||
score: string
|
||||
winScore: number
|
||||
type: 0 | 1
|
||||
players: string
|
||||
team: string
|
||||
scoreList: string | null
|
||||
}
|
||||
|
||||
export type HistoryListItem = {
|
||||
id: number
|
||||
time: number
|
||||
playedAt: string
|
||||
dayOfWeek: number
|
||||
dayLabel: string
|
||||
score: [number, number]
|
||||
winScore: number
|
||||
type: 0 | 1
|
||||
typeLabel: string
|
||||
players: string[]
|
||||
team: [string[], string[]]
|
||||
scoreList: Array<[number, number, number, 0 | 1]>
|
||||
leftTeamName: string
|
||||
rightTeamName: string
|
||||
winnerTeamName: string
|
||||
}
|
||||
|
||||
export type LiveRoomStatus = 'live' | 'finished'
|
||||
|
||||
export type LiveRoomSession = {
|
||||
hostToken: string
|
||||
roomId: string
|
||||
status: LiveRoomStatus
|
||||
}
|
||||
|
||||
export type LiveRoomSummary = {
|
||||
roomId: string
|
||||
createdAt: string
|
||||
leftTeamName: string
|
||||
rightTeamName: string
|
||||
scoreLeft: number
|
||||
scoreRight: number
|
||||
status: LiveRoomStatus
|
||||
targetScore: number
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type LiveRoomPayload = {
|
||||
groupId: number | null
|
||||
leftTeamName: string
|
||||
matchupLabel: string
|
||||
pointLog: PointHistoryEntry[]
|
||||
rightTeamName: string
|
||||
scoreState: ScoreState
|
||||
targetDate: string
|
||||
}
|
||||
|
||||
export type LiveRoomDetail = LiveRoomPayload & {
|
||||
createdAt: string
|
||||
roomId: string
|
||||
status: LiveRoomStatus
|
||||
updatedAt: string
|
||||
winnerTeamName: string | null
|
||||
}
|
||||
|
||||
export type LiveRoomUpdatePayload = LiveRoomPayload & {
|
||||
hostToken: string
|
||||
status: LiveRoomStatus
|
||||
winnerTeamName: string | null
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user