修正記分板在 iPad 尺寸的版面與修復 dev server 啟動問題
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -25,3 +25,4 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
/*.stackdump
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
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 {
|
: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
302
src/types.ts
302
src/types.ts
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user