feat: improve draft admin UI, draft state sync, and styling
Major refactor of Draft admin and participant Websocket state sync Use consistent state dict serialization in DraftStateManager (to_dict, dict-like access, etc.) Always include up-to-date participants and draft status in sync payloads Draft phase/order summary now sent as objects instead of calling .get_summary() UI/UX updates: Updated DraftAdmin.jsx: Connects DraftParticipant panel for real-time participant state Centralizes phase advance, bidding, and sync controls Moves phase selector into a dedicated panel Refine markup/extends in room_admin.dj.html (use block body, fix root data attribute) Minor fixes to DraftCountdownClock.jsx to robustly handle NaN time CSS/layout: Refactor .draft-participant styling to .wrapper within #draft-participant-root and #draft-admin-root for better responsive layout and code clarity Server code: Simplify draft consumer/manager state interaction, drop unused cache keys, update order determination and phase management, and ensure DRY status object responses Small code style and consistency cleanups Misc: Add debugpy launch task in code-workspace and clean workspace JSON (style/consistency) Minor formatting and error handling improvements
This commit is contained in:
@@ -7,6 +7,7 @@ import { DraftMessage, DraftPhase, DraftPhaseLabel, DraftPhasesOrdered } from '.
|
||||
import { fetchDraftDetails, isEmptyObject, handleDraftStatusMessages, handleUserIdentifyMessages } from "../common/utils.js"
|
||||
import { DraftMoviePool } from "../common/DraftMoviePool.jsx"
|
||||
import { DraftCountdownClock } from "../common/DraftCountdownClock.jsx"
|
||||
import { DraftParticipant } from "../participant/DraftParticipant.jsx";
|
||||
import { jsxs } from "react/jsx-runtime";
|
||||
|
||||
|
||||
@@ -14,7 +15,6 @@ import { jsxs } from "react/jsx-runtime";
|
||||
const DraftPhaseDisplay = ({ draftPhase, nextPhaseHandler, prevPhaseHandler }) => {
|
||||
return (
|
||||
<div className="draft-phase-container">
|
||||
<label>Phase</label>
|
||||
<div className="d-flex">
|
||||
<div className="change-phase"><button onClick={prevPhaseHandler}><i className="bi bi-chevron-left"></i></button></div>
|
||||
<ol>
|
||||
@@ -46,19 +46,19 @@ export const DraftAdmin = ({ draftSessionId }) => {
|
||||
})
|
||||
}, [])
|
||||
|
||||
useEffect(()=>{
|
||||
useEffect(() => {
|
||||
if (!socket) return;
|
||||
const openHandler = (event)=>{
|
||||
const openHandler = (event) => {
|
||||
console.log('Websocket Opened')
|
||||
}
|
||||
const closeHandler = (event)=>{
|
||||
const closeHandler = (event) => {
|
||||
console.log('Websocket Closed')
|
||||
}
|
||||
socket.addEventListener('open', openHandler );
|
||||
socket.addEventListener('close', closeHandler );
|
||||
return ()=>{
|
||||
socket.removeEventListener('open', openHandler );
|
||||
socket.removeEventListener('close', closeHandler );
|
||||
socket.addEventListener('open', openHandler);
|
||||
socket.addEventListener('close', closeHandler);
|
||||
return () => {
|
||||
socket.removeEventListener('open', openHandler);
|
||||
socket.removeEventListener('close', closeHandler);
|
||||
}
|
||||
}, [socket])
|
||||
|
||||
@@ -67,7 +67,7 @@ export const DraftAdmin = ({ draftSessionId }) => {
|
||||
|
||||
const draftStatusMessageHandler = (event) => handleDraftStatusMessages(event, setDraftState)
|
||||
const userIdentifyMessageHandler = (event) => handleUserIdentifyMessages(event, setCurrentUser)
|
||||
const handleNominationRequest = (event)=> {
|
||||
const handleNominationRequest = (event) => {
|
||||
const message = JSON.parse(event.data)
|
||||
const { type, payload } = message;
|
||||
if (type == DraftMessage.NOMINATION_SUBMIT_REQUEST) {
|
||||
@@ -79,15 +79,15 @@ export const DraftAdmin = ({ draftSessionId }) => {
|
||||
))
|
||||
}
|
||||
}
|
||||
socket.addEventListener('message', draftStatusMessageHandler );
|
||||
socket.addEventListener('message', userIdentifyMessageHandler );
|
||||
socket.addEventListener('message', handleNominationRequest );
|
||||
|
||||
|
||||
socket.addEventListener('message', draftStatusMessageHandler);
|
||||
socket.addEventListener('message', userIdentifyMessageHandler);
|
||||
socket.addEventListener('message', handleNominationRequest);
|
||||
|
||||
|
||||
return () => {
|
||||
socket.removeEventListener('message', draftStatusMessageHandler)
|
||||
socket.removeEventListener('message', userIdentifyMessageHandler );
|
||||
socket.removeEventListener('message', handleNominationRequest );
|
||||
socket.removeEventListener('message', userIdentifyMessageHandler);
|
||||
socket.removeEventListener('message', handleNominationRequest);
|
||||
};
|
||||
}, [socket]);
|
||||
|
||||
@@ -134,36 +134,31 @@ export const DraftAdmin = ({ draftSessionId }) => {
|
||||
const handleStartBidding = () => {
|
||||
socket.send(
|
||||
JSON.stringify(
|
||||
{type: DraftMessage.BID_START_REQUEST}
|
||||
{ type: DraftMessage.BID_START_REQUEST }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container draft-panel admin">
|
||||
<div className="d-flex justify-content-between border-bottom mb-2 p-1">
|
||||
<h3>Draft Panel</h3>
|
||||
<div className="d-flex gap-1">
|
||||
<WebSocketStatus socket={socket} />
|
||||
<button onClick={() => handleRequestDraftSummary()} className="btn btn-small btn-light">
|
||||
<i className="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
<div className="">
|
||||
<div className="">
|
||||
<DraftParticipant draftSessionId={draftSessionId}></DraftParticipant>
|
||||
<div className="d-flex justify-content-between border-bottom mb-2 p-1">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ParticipantList
|
||||
currentUser = {currentUser}
|
||||
draftState={draftState}
|
||||
draftDetails={draftDetails}
|
||||
isAdmin={true}
|
||||
/>
|
||||
<div className="d-flex gap-1 m-1">
|
||||
<button onClick={handleAdvanceDraft} className="btn btn-primary">Advance Draft</button>
|
||||
<button onClick={handleStartBidding} className="btn btn-primary">Start Bidding</button>
|
||||
<section className="d-flex justify-content-center mt-3">
|
||||
<button onClick={() => handleRequestDraftSummary()} className="btn btn-small btn-light mx-1">
|
||||
<i className="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
<button onClick={handleAdvanceDraft} className="btn btn-primary mx-1">Advance Index</button>
|
||||
<button onClick={handleStartBidding} className="btn btn-primary mx-1">Start Bidding</button>
|
||||
</section>
|
||||
|
||||
<div class="d-flex justify-content-center mt-3">
|
||||
<DraftPhaseDisplay draftPhase={draftState.phase} nextPhaseHandler={() => { handlePhaseChange('next') }} prevPhaseHandler={() => { handlePhaseChange('previous') }}></DraftPhaseDisplay>
|
||||
</div>
|
||||
<DraftMoviePool draftDetails={draftDetails} draftState={draftState}></DraftMoviePool>
|
||||
<DraftPhaseDisplay draftPhase={draftState.phase} nextPhaseHandler={() => { handlePhaseChange('next') }} prevPhaseHandler={() => { handlePhaseChange('previous') }}></DraftPhaseDisplay>
|
||||
<DraftCountdownClock endTime={draftState.bidding_timer_end}></DraftCountdownClock>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -27,7 +27,7 @@ export function DraftCountdownClock({ endTime, onFinish }) {
|
||||
return (
|
||||
<div className="countdown-clock">
|
||||
<span>
|
||||
{minutes}:{pad(secs)}
|
||||
{!isNaN(minutes) && !isNaN(secs) ? `${minutes}:${pad(secs)}` : "0:00"}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -85,7 +85,7 @@ export const DraftParticipant = ({ draftSessionId }) => {
|
||||
}, [socket]);
|
||||
|
||||
return (
|
||||
<div className="draft-participant">
|
||||
<div className="wrapper">
|
||||
<section class="panel draft-live">
|
||||
<header class="panel-header d-flex justify-content-between align-items-center">
|
||||
<h2 class="panel-title">Draft Live</h2>
|
||||
|
||||
@@ -124,30 +124,34 @@
|
||||
}
|
||||
}
|
||||
|
||||
.draft-participant {
|
||||
display: flex;
|
||||
flex-wrap: wrap; /* allow panels to wrap */
|
||||
gap: 1rem; /* space between panels */
|
||||
justify-content: center; /* center the panels horizontally */
|
||||
#draft-participant-root,
|
||||
#draft-admin-root {
|
||||
@extend .flex-grow-1;
|
||||
.wrapper:first-child {
|
||||
display: flex;
|
||||
flex-wrap: wrap; /* allow panels to wrap */
|
||||
gap: 1rem; /* space between panels */
|
||||
justify-content: center; /* center the panels horizontally */
|
||||
|
||||
.panel {
|
||||
flex: 1 1 350px; /* grow/shrink, base width */
|
||||
max-width: 450px; /* never go beyond this */
|
||||
min-width: 300px; /* keeps them from getting too small */
|
||||
}
|
||||
.panel.draft-live {
|
||||
.draft-live-state-container {
|
||||
@extend .d-flex;
|
||||
.countdown-clock {
|
||||
@extend .fs-1;
|
||||
@extend .fw-bold;
|
||||
@extend .col;
|
||||
@extend .align-content-center;
|
||||
@extend .text-center;
|
||||
}
|
||||
.pick-description{
|
||||
@extend .col;
|
||||
.panel {
|
||||
flex: 1 1 350px; /* grow/shrink, base width */
|
||||
max-width: 450px; /* never go beyond this */
|
||||
min-width: 300px; /* keeps them from getting too small */
|
||||
}
|
||||
.panel.draft-live {
|
||||
.draft-live-state-container {
|
||||
@extend .d-flex;
|
||||
.countdown-clock {
|
||||
@extend .fs-1;
|
||||
@extend .fw-bold;
|
||||
@extend .col;
|
||||
@extend .align-content-center;
|
||||
@extend .text-center;
|
||||
}
|
||||
.pick-description {
|
||||
@extend .col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user