From 61ac30b3b63e9ee64c243d623346d508c0c48a25 Mon Sep 17 00:00:00 2001 From: Daniel Fosco Date: Tue, 12 Dec 2023 17:56:13 +0100 Subject: [PATCH 1/3] Iterate handleSplitUsers so it always tries to add user to room with least number of users --- .../src/components/BreakoutManager/BreakoutManager.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx index 1d42d9f89..ef37b02b5 100644 --- a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx +++ b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx @@ -142,8 +142,11 @@ export const BreakoutManager: React.FC = () => { usersInRooms.push(users.slice(i, i + roomSize)); } + // Sort rooms by number of participants in ascending order + const sortedRooms = [...rooms].sort((a, b) => a.participants.length - b.participants.length); + for (let i = 0; i < count; ++i) { - const room = rooms[i]; + const room = sortedRooms[i]; const participants = usersInRooms[i]; for (const participant of participants) { await service.addParticipant(room, participant); From af7caf01cd3165f29c9e25fcf65bd1f328fabced Mon Sep 17 00:00:00 2001 From: Daniel Fosco Date: Tue, 19 Dec 2023 14:04:18 +0100 Subject: [PATCH 2/3] [Breakout Rooms] UI and UX improvements (#235) * Fix UI on empty state button * Fix list of validations to always show missing steps * Improve style in validations * Make copy in user(s) splitting block dynamic * Update 'Waiting room' copy on user selection dropdown * UI and copy fixes in Room block * Button size adjusted in waiting room * Improve copy and UI for frame selection * Fix rooms container height * Fix copy * Increase timer limit and add +5min option * CSS fixes * Remove CSS nesting * Update toast copy * Update examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx Co-authored-by: Mettin Parzinski * Update examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx Co-authored-by: Mettin Parzinski * Update examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx Co-authored-by: Frederico Estrela * Iterate frame title rendering when set to room * Update styles * Update relative timer rendering to change timer button text Update finish session button variant * Improve styles and dropdown separator * One more style fix * One more more style fix * Update examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx Co-authored-by: Mettin Parzinski * Fix broken copy * Add CSS class to avoid li+li --------- Co-authored-by: Mettin Parzinski Co-authored-by: Frederico Estrela --- .../src/components/Avatar/Avatar.css | 6 +- .../BreakoutManager/BreakoutManager.css | 30 +++++++--- .../BreakoutManager/BreakoutManager.tsx | 36 +++++++----- .../BreakoutStarter/BreakoutStarter.css | 37 ++++++++++-- .../BreakoutStarter/BreakoutStarter.tsx | 15 ++--- .../src/components/RoomConfig/RoomConfig.css | 16 +++-- .../src/components/RoomConfig/RoomConfig.tsx | 36 +++++++++--- .../components/RoomsManager/RoomsManager.css | 6 +- .../src/components/Timer/Timer.css | 1 - .../src/components/Timer/Timer.tsx | 58 ++++++++++++++----- .../components/WaitingList/WaitingList.css | 8 +-- .../components/WaitingList/WaitingList.tsx | 3 +- .../components/WaitingRoom/WaitingRoom.css | 4 +- .../components/WaitingRoom/WaitingRoom.tsx | 2 +- examples/breakout-rooms/src/hooks.tsx | 4 +- examples/breakout-rooms/src/types.ts | 5 +- examples/breakout-rooms/src/utils.ts | 19 +++--- 17 files changed, 187 insertions(+), 99 deletions(-) diff --git a/examples/breakout-rooms/src/components/Avatar/Avatar.css b/examples/breakout-rooms/src/components/Avatar/Avatar.css index 080f656fc..af0b02e43 100644 --- a/examples/breakout-rooms/src/components/Avatar/Avatar.css +++ b/examples/breakout-rooms/src/components/Avatar/Avatar.css @@ -1,13 +1,13 @@ .avatar { border-radius: 100%; flex-shrink: 0; - background: #817f99; + background: var(--indigo600); box-shadow: 0 0 0 6px var(--color, transparent); - outline: 4px solid #fff; + outline: 4px solid var(--white); width: 2.2em; height: 2.2em; font-size: 14px; - color: #fff; + color: var(--white); display: flex; justify-content: center; align-items: center; diff --git a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.css b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.css index 9cfcc545f..b590b0c02 100644 --- a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.css +++ b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.css @@ -2,36 +2,48 @@ display: flex; height: 100%; flex-direction: column; - gap: 1em; + gap: var(--space-small); } .validation-messages { - background: #f5f5f3; - padding: 1em; + background: var(--indigo50); + border-radius: var(--border-radius-medium); + padding: var(--space-medium); } -.validation-messages .validatino-messages-title { +.validation-messages .validation-messages-title { font-size: 1em; font-weight: 600; - line-height: 1em; + line-height: var(--space-small); margin: 0; - color: #656b81; + color: var(--indigo600); } -.validation-messages .validatino-messages-items { +.validation-messages .validation-messages-list { font-size: 0.9em; + padding-left: var(--space-small); + margin-bottom: 0; +} + +.validation-messages-item + .validation-messages-item { + margin-top: var(--space-xsmall); +} + +.validation-messages-item::marker { + color: var(--indigo500); + padding-left: var(--space-small); } .toolbar { display: flex; flex-direction: column; justify-content: center; - gap: 1em; + gap: var(--space-small); } .toolbar button { justify-content: center; margin: 0; width: 100%; - padding: 1em; + padding: var(--space-small); } diff --git a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx index ef37b02b5..56f320c7f 100644 --- a/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx +++ b/examples/breakout-rooms/src/components/BreakoutManager/BreakoutManager.tsx @@ -68,13 +68,13 @@ export const BreakoutManager: React.FC = () => { if (frame) { await service.setRoomTarget(selectedRoom, frame.id); await miro.board.notifications.showInfo( - `Frame "${frame.title}" has been selected as starting point.`, + `${frame.title} is set as starting point for the room`, ); await miro.board.deselect({ id: frame.id }); setSelectedRoom(undefined); } else { await miro.board.notifications.showError( - "We only support frames as starting point for now", + "Only frames are supported as starting point for rooms", ); } }; @@ -87,7 +87,7 @@ export const BreakoutManager: React.FC = () => { const handleNudge = async (currentUser?: Json) => { if (isUser(currentUser)) { await miro.board.notifications.showInfo( - `${currentUser?.name} is waiting to start the session`, + `${currentUser?.name} is waiting for you to start the session`, ); } }; @@ -111,7 +111,7 @@ export const BreakoutManager: React.FC = () => { setSelectedRoom(selected); await miro.board.notifications.showInfo( - `Select the frame in the board for ${selected.name}`, + `Set a frame to the room by clicking on the frame title`, ); }; @@ -186,25 +186,31 @@ export const BreakoutManager: React.FC = () => { }); }; + const valRooms = "Add rooms to your session"; + const valUsers = "Add users to each room"; + const valFrames = "Set a frame to each room"; + const validations: string[] = []; if (!breakout?.rooms.length) { - validations.push("Add rooms to your session"); + validations.push(valRooms); + validations.push(valUsers); + validations.push(valFrames); } const allRoomsWithParticipants = breakout?.rooms.every( (room) => room.participants.length > 0, ); - if (!allRoomsWithParticipants) { - validations.push("Add users to each room"); + if (!allRoomsWithParticipants && !validations.includes(valUsers)) { + validations.push(valUsers); } const allRoomsWithTargets = breakout?.rooms.every((room) => Boolean(room.targetId), ); - if (!allRoomsWithTargets) { - validations.push("Set a frame to each room"); + if (!allRoomsWithTargets && !validations.includes(valFrames)) { + validations.push(valFrames); } const canStartSession = validations.length < 1; @@ -242,12 +248,14 @@ export const BreakoutManager: React.FC = () => { {isEditable && validations.length > 0 ? (
-
+
Before starting the session:
-
    +
      {validations.map((message) => ( -
    • {message}
    • +
    • + {message} +
    • ))}
@@ -256,10 +264,10 @@ export const BreakoutManager: React.FC = () => { {breakout?.state === "started" ? ( diff --git a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.css b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.css index 2fb38c70b..40b902aff 100644 --- a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.css +++ b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.css @@ -2,12 +2,12 @@ display: flex; flex-direction: column; justify-content: center; - background: #eeeded; - border-radius: 0.25em; + background: var(--indigo50); + border-radius: var(--border-radius-medium); gap: 0.5em; - padding: 1em; + padding: var(--space-small); + padding-top: var(--space-xsmall); width: 100%; - height: 8em; } .room-controls { @@ -43,13 +43,16 @@ .users .list { /** Overrides DS specific tokens for dropdown **/ --space-inset-100: 16px; - font-size: 12px; +} + +.users .list .separator { + margin: var(--space-xsmall) var(--space-xxsmall); } .users .list .item { display: flex; align-items: center; - gap: 1em; + gap: var(--space-small); } .users .list .item + div { padding-left: 3.5em; @@ -57,6 +60,7 @@ .avatars { display: flex; + padding-left: var(--space-xxsmall); } .avatars .avatar:not(:first-child) { diff --git a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx index 6ab4d318a..ca7ccb960 100644 --- a/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx +++ b/examples/breakout-rooms/src/components/RoomConfig/RoomConfig.tsx @@ -8,7 +8,7 @@ import { IconTrash, IconUserAdd, } from "@mirohq/design-system"; -import { OnlineUserInfo } from "@mirohq/websdk-types"; +import { Frame, OnlineUserInfo } from "@mirohq/websdk-types"; import type { Participant, Room } from "../../types"; import { Avatar } from "../Avatar"; @@ -35,6 +35,24 @@ export const RoomConfig: React.FunctionComponent = ({ onAddParticipant, onRemoveParticipant, }) => { + const [title, setName] = React.useState(""); + React.useEffect(() => { + async function FetchFrame(): Promise { + if (!room.targetId) { + return; + } + const frame = (await miro.board.getById(room.targetId)) as Frame; + setName(frame?.title); + } + FetchFrame(); + }, [room.targetId]); + + const renderedTitle = title ? `${title}` : "frame"; + const dropdownSeparator = ( +
+ +
+ ); return (
@@ -42,8 +60,12 @@ export const RoomConfig: React.FunctionComponent = ({ {room.name} onSelectTarget(room)} > @@ -51,7 +73,7 @@ export const RoomConfig: React.FunctionComponent = ({ onRemove(room)} @@ -72,7 +94,7 @@ export const RoomConfig: React.FunctionComponent = ({ @@ -88,7 +110,7 @@ export const RoomConfig: React.FunctionComponent = ({ {unassignedUsers.length ? (
- Users not in the room + Users not in rooms {unassignedUsers.map((user) => ( = ({ {room.participants.length ? (
- {unassignedUsers.length ? : null} + {unassignedUsers.length ? dropdownSeparator : null} {room.participants.map((user) => ( = ({ minTime = DEFAULT_MIN_TIME, maxTime = DEFAULT_MAX_TIME, step = DEFAULT_STEP, + bigStep = BIG_STEP, + bigStepMaxTime = BIG_STEP_MAX_TIME, onSet, }) => { const [time, setTime] = React.useState(defaultTime); @@ -70,21 +88,27 @@ export const Timer: React.FunctionComponent = ({ setTime((time) => (time > minTime ? time - step : time)); }; + const handleBigAdd = () => { + setTime((time) => (time <= bigStepMaxTime ? time + bigStep : time)); + }; + const handleSetTime = () => { onSet(time); setTimeChanged(true); }; + const minutes = formatTime(time) > 1 ? "minutes" : "minute"; + return ( <> - + handleSetTime()}> @@ -92,8 +116,8 @@ export const Timer: React.FunctionComponent = ({
+
-
diff --git a/examples/breakout-rooms/src/components/WaitingList/WaitingList.css b/examples/breakout-rooms/src/components/WaitingList/WaitingList.css index 1716975ae..c11123f3b 100644 --- a/examples/breakout-rooms/src/components/WaitingList/WaitingList.css +++ b/examples/breakout-rooms/src/components/WaitingList/WaitingList.css @@ -1,10 +1,10 @@ .waiting-list { - border: 1px solid #d4d4d9; - padding: 1em; - border-radius: 0.25em; + border: 1px solid var(--indigo300); + padding: var(--space-small); + border-radius: var(--border-radius-medium); display: flex; flex-direction: column; - gap: 1em; + gap: var(--space-small); } .waiting-list .waiting-list-title { diff --git a/examples/breakout-rooms/src/components/WaitingList/WaitingList.tsx b/examples/breakout-rooms/src/components/WaitingList/WaitingList.tsx index f62082f47..dfcdddfe2 100644 --- a/examples/breakout-rooms/src/components/WaitingList/WaitingList.tsx +++ b/examples/breakout-rooms/src/components/WaitingList/WaitingList.tsx @@ -18,7 +18,8 @@ export const WaitingList: React.FC = ({ return (
- {unassignedUsers.length} user(s) not in rooms + {unassignedUsers.length} {unassignedUsers.length > 1 ? "users" : "user"}{" "} + not in rooms