From c7d0ec6215b7280a50ace44d3d6b6873cd90467c Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Sat, 20 Jun 2026 23:29:50 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20modalit=C3=A0=20estesa=20controller=20c?= =?UTF-8?q?on=20dashboard=20landscape?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aggiunge una seconda modalità dashboard per schermi orizzontali, rilevata automaticamente dall'orientamento (landscape → estesa, portrait → mobile) tramite resize/orientationchange listener. Layout estesa: due pannelli affiancati con stripe colorata in cima (giallo/blu per identità squadra), score grande come elemento eroe, diagramma campo in verde scuro, SET button a colori pieni in fondo. Barra azioni compatta (40px) con tutti i controlli secondari. Il pannello usa position:fixed ancorato al viewport per impedire qualsiasi scroll, con @wheel.prevent e @touchmove.prevent. --- .claude/settings.json | 3 +- src/components/ControllerPage.vue | 415 +++++++++++++++++++++++++----- 2 files changed, 352 insertions(+), 66 deletions(-) diff --git a/.claude/settings.json b/.claude/settings.json index c601f37..1fe418f 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,6 +1,7 @@ { "enabledPlugins": { "claude-md-management@claude-plugins-official": true, - "code-simplifier@claude-plugins-official": true + "code-simplifier@claude-plugins-official": true, + "frontend-design@claude-plugins-official": true } } diff --git a/src/components/ControllerPage.vue b/src/components/ControllerPage.vue index 8bf0a82..b7b56b4 100644 --- a/src/components/ControllerPage.vue +++ b/src/components/ControllerPage.vue @@ -6,66 +6,147 @@ {{ wsConnected ? 'Connesso' : 'Connessione...' }} - -
-
-
{{ state.sp.nomi[primaSquadra] }}
-
{{ punt[primaSquadra] }}
-
SET {{ set[primaSquadra] }}
- Servizio + + + + +
+
+ + +
+
+
+
+ {{ state.sp.nomi[primaSquadra] }} + + + {{ set[primaSquadra] }} SET + +
+
+ {{ punt[primaSquadra] }} +
+
+
+
{{ state.sp.form[primaSquadra][x] }}
+
+
+
+
{{ state.sp.form[primaSquadra][x] }}
+
+
+ +
+
+ + +
+
+
+
+ {{ state.sp.nomi[secondaSquadra] }} + + + {{ set[secondaSquadra] }} SET + +
+
+ {{ punt[secondaSquadra] }} +
+
+
+
{{ state.sp.form[secondaSquadra][x] }}
+
+
+
+
{{ state.sp.form[secondaSquadra][x] }}
+
+
+ +
+
+
+ + +
+ + + + + + + +
- -
- -
- - -
- - -
- - -
- - - - - - - - -
+
@@ -213,9 +294,11 @@ export default { formHome: ["1", "2", "3", "4", "5", "6"], formGuest: ["1", "2", "3", "4", "5", "6"], }, + isLandscape: window.innerWidth > window.innerHeight, } }, computed: { + isEstesa() { return this.isLandscape }, primaSquadra() { return this.state.order ? 'home' : 'guest' }, secondaSquadra() { return this.state.order ? 'guest' : 'home' }, isPunteggioZeroZero() { @@ -255,6 +338,15 @@ export default { } }, }, + mounted() { + this._resizeHandler = () => { this.isLandscape = window.innerWidth > window.innerHeight } + window.addEventListener('resize', this._resizeHandler) + window.addEventListener('orientationchange', this._resizeHandler) + }, + beforeUnmount() { + window.removeEventListener('resize', this._resizeHandler) + window.removeEventListener('orientationchange', this._resizeHandler) + }, methods: { onWsMessage(msg) { if (msg.type === 'error') this.showErrorFeedback(msg.message) @@ -370,6 +462,7 @@ export default { font-family: 'Inter', system-ui, sans-serif; -webkit-user-select: none; user-select: none; + overflow-x: hidden; } /* Barra stato connessione */ @@ -400,7 +493,8 @@ export default { background: white; } -/* Anteprima punteggio */ +/* ── MODALITÀ MOBILE ── */ + .score-preview { display: flex; gap: 8px; @@ -459,7 +553,6 @@ export default { height: 20px; } -/* Riga annulla punto */ .undo-row { margin-bottom: 8px; } @@ -478,7 +571,6 @@ export default { background: rgba(255,100,50,0.2); } -/* Pulsanti set */ .action-row { display: flex; gap: 8px; @@ -498,7 +590,6 @@ export default { transform: scale(0.97); } -/* Griglia controlli */ .controls { display: grid; grid-template-columns: repeat(2, 1fr); @@ -529,6 +620,7 @@ export default { opacity: 0.35; } + .btn-danger { background: rgba(198, 40, 40, 0.25); border: 1px solid rgba(239, 83, 80, 0.4); @@ -542,7 +634,195 @@ export default { background: rgba(198, 40, 40, 0.45); } -/* Overlay e finestre modali */ +/* ── MODALITÀ ESTESA ── */ + +.e-dash { + position: fixed; + top: 28px; /* altezza conn-bar */ + left: 0; + right: 0; + bottom: 0; + display: flex; + flex-direction: column; + padding: 10px; + gap: 8px; + box-sizing: border-box; + overflow: hidden; + background: #111; +} + +.e-panels { + display: flex; + flex: 1; + gap: 10px; + min-height: 0; +} + +/* Pannello squadra */ +.e-panel { + flex: 1; + display: flex; + flex-direction: column; + border-radius: 16px; + overflow: hidden; + background: #161618; + border: 1px solid rgba(255,255,255,0.07); + min-height: 0; +} + +/* Barra colorata in cima — identità squadra */ +.e-panel__stripe { + height: 5px; + flex-shrink: 0; +} +.e-panel--home .e-panel__stripe { background: #f5c518; } +.e-panel--guest .e-panel__stripe { background: #2196f3; } + +.e-panel__body { + flex: 1; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 14px 16px 14px; + min-height: 0; +} + +/* Header */ +.e-panel__head { + display: flex; + align-items: center; + justify-content: space-between; + flex-shrink: 0; +} +.e-panel__name { + font-size: 15px; + font-weight: 900; + text-transform: uppercase; + letter-spacing: 1.5px; + color: #f0f0f0; +} +.e-panel__meta { + display: flex; + align-items: center; + gap: 5px; + font-size: 12px; + font-weight: 700; +} +.e-panel--home .e-panel__meta { color: #f5c518; } +.e-panel--guest .e-panel__meta { color: #2196f3; } +.e-label { + font-size: 9px; + letter-spacing: 1px; + opacity: 0.6; + font-weight: 600; +} +.e-panel__serv { width: 14px; height: 14px; } + +/* Score — l'elemento eroe */ +.e-panel__score { + font-size: clamp(60px, 14vh, 110px); + font-weight: 900; + line-height: 1; + text-align: center; + cursor: pointer; + border-radius: 12px; + padding: 6px 0; + flex-shrink: 0; + font-variant-numeric: tabular-nums; + transition: opacity 0.1s, transform 0.1s; + user-select: none; +} +.e-panel__score:active { opacity: 0.65; transform: scale(0.96); } +.e-panel--home .e-panel__score { color: #f5c518; } +.e-panel--guest .e-panel__score { color: #2196f3; } + +/* Campo formazione — diagramma del campo */ +.e-panel__court { + flex-shrink: 0; + display: flex; + flex-direction: column; + gap: 0; + background: rgba(34, 80, 34, 0.18); + border: 1px solid rgba(255,255,255,0.08); + border-radius: 10px; + padding: 8px 10px; + height: clamp(120px, 28vh, 200px); +} +.e-court__row { + flex: 1; + display: flex; + gap: 6px; + min-height: 0; +} +.e-court__cell { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + font-size: clamp(13px, 2.4vh, 22px); + font-weight: 700; + color: #ddd; + background: rgba(255,255,255,0.07); + border-radius: 6px; + min-height: 0; +} +.e-court__net { + height: 2px; + background: rgba(255,255,255,0.18); + margin: 5px 0; + border-radius: 1px; + flex-shrink: 0; +} + +/* SET button — pieno colore, fondo pannello */ +.e-panel__setbtn { + flex-shrink: 0; + width: 100%; + padding: 9px 0; + font-size: 11px; + font-weight: 800; + letter-spacing: 1.2px; + text-transform: uppercase; + border: none; + border-radius: 8px; + cursor: pointer; + transition: opacity 0.1s; +} +.e-panel__setbtn:active { opacity: 0.75; transform: scale(0.98); } +.e-panel--home .e-panel__setbtn { background: #f5c518; color: #111; } +.e-panel--guest .e-panel__setbtn { background: #2196f3; color: #fff; } + +/* Barra azioni secondarie */ +.e-actions { + display: flex; + gap: 6px; + flex-shrink: 0; + height: 40px; +} +.e-act { + flex: 1; + height: 100%; + font-family: inherit; + font-size: 11px; + font-weight: 600; + letter-spacing: 0.2px; + border-radius: 8px; + border: 1px solid rgba(255,255,255,0.1); + background: rgba(255,255,255,0.07); + color: #bbb; + cursor: pointer; + white-space: nowrap; + -webkit-tap-highlight-color: transparent; + transition: background 0.12s; +} +.e-act:active { background: rgba(255,255,255,0.16); } +.e-act:disabled { opacity: 0.28; cursor: not-allowed; } +.e-act--undo { color: #ffab91; border-color: rgba(255,171,145,0.2); } +.e-act--danger { background: rgba(180,30,30,0.2); border-color: rgba(239,83,80,0.3); color: #ff6b6b; } +.e-act--danger:active { background: rgba(198,40,40,0.4); } + +/* ── OVERLAY E DIALOGS ── */ + .overlay { position: fixed; top: 0; @@ -608,6 +888,9 @@ export default { border-radius: 12px; font-size: 15px; font-weight: 600; + border: none; + cursor: pointer; + font-family: inherit; } .btn-confirm { @@ -618,12 +901,14 @@ export default { border-radius: 12px; font-size: 15px; font-weight: 700; + border: none; + cursor: pointer; + font-family: inherit; } .btn-confirm:disabled { opacity: 0.4; } -/* Gruppi form */ .form-group { margin-bottom: 16px; } @@ -667,7 +952,6 @@ export default { border-color: #64b5f6; } -/* Griglia formazione */ .form-grid { background: rgba(205, 133, 63, 0.15); border: 2px solid rgba(255,255,255,0.15); @@ -685,7 +969,6 @@ export default { margin: 4px 0; } -/* Pulsanti modalita */ .mode-buttons { display: flex; gap: 8px; @@ -699,13 +982,15 @@ export default { font-size: 16px; font-weight: 700; transition: all 0.15s; + border: none; + cursor: pointer; + font-family: inherit; } .btn-mode.active { background: #2e7d32; color: white; } -/* Sezione cambi */ .cambi-container { display: flex; flex-direction: column;