diff --git a/package-lock.json b/package-lock.json index 4f866634..e34ace8e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,11 +15,13 @@ "devDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", + "@mui/x-date-pickers": "^7.2.0", "@playwright/test": "^1.43.1", "@types/jest": "^29.5.11", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.8.0", "all-contributors-cli": "^6.26.1", + "dayjs": "^1.11.10", "eslint": "^8.57.0", "eslint-config-next": "^14.2.2", "eslint-config-prettier": "^9.1.0", @@ -626,9 +628,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.4.tgz", + "integrity": "sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2908,9 +2910,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.7.tgz", - "integrity": "sha512-AuF+Wo2Mp/edaO6vJnWjg+gj4tzEz5ChMZnAQpc22DXpSvM8ddgGcZvM7D7F99pIBoSv8ub+Iz0viL+yuGVmhg==", + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.15.tgz", + "integrity": "sha512-aXnw29OWQ6I5A47iuWEI6qSSUfH6G/aCsW9KmW3LiFqr7uXZBK4Ks+z8G+qeIub8k0T5CMqlT2q0L+ZJTMrqpg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" @@ -2982,19 +2984,19 @@ } }, "node_modules/@mui/material": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.7.tgz", - "integrity": "sha512-l6+AiKZH3iOJmZCnlpel8ghYQe9Lq0BEuKP8fGj3g5xz4arO9GydqYAtLPMvuHKtArj8lJGNuT2yHYxmejincA==", + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.15.tgz", + "integrity": "sha512-3zvWayJ+E1kzoIsvwyEvkTUKVKt1AjchFFns+JtluHCuvxgKcLSRJTADw37k0doaRtVAsyh8bz9Afqzv+KYrIA==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/base": "5.0.0-beta.34", - "@mui/core-downloads-tracker": "^5.15.7", - "@mui/system": "^5.15.7", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.7", + "@mui/base": "5.0.0-beta.40", + "@mui/core-downloads-tracker": "^5.15.15", + "@mui/system": "^5.15.15", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", "@types/react-transition-group": "^4.4.10", "clsx": "^2.1.0", - "csstype": "^3.1.2", + "csstype": "^3.1.3", "prop-types": "^15.8.1", "react-is": "^18.2.0", "react-transition-group": "^4.4.5" @@ -3025,13 +3027,44 @@ } } }, + "node_modules/@mui/material/node_modules/@mui/base": { + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/private-theming": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.7.tgz", - "integrity": "sha512-bcEeeXm7GyQCQvN9dwo8htGv8/6tP05p0i02Z7GXm5EoDPlBcqTNGugsjNLoGq6B0SsdyanjJGw0Jw00o1yAOA==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", + "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.7", + "@mui/utils": "^5.15.14", "prop-types": "^15.8.1" }, "engines": { @@ -3052,13 +3085,13 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.7.tgz", - "integrity": "sha512-ixSdslOjK1kzdGcxqj7O3d14By/LPQ7EWknsViQ8RaeT863EAQemS+zvUJDTcOpkfJh6q6gPnYMIb2TJCs9eWA==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", + "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", "dependencies": { "@babel/runtime": "^7.23.9", "@emotion/cache": "^11.11.0", - "csstype": "^3.1.2", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -3083,17 +3116,17 @@ } }, "node_modules/@mui/system": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.7.tgz", - "integrity": "sha512-9alZ4/dLxsTwUOdqakgzxiL5YW6ntqj0CfzWImgWnBMTZhgGcPsbYpBLniNkkk7/jptma4/bykWXHwju/ls/pg==", + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", + "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.7", - "@mui/styled-engine": "^5.15.7", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.7", + "@mui/private-theming": "^5.15.14", + "@mui/styled-engine": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", "clsx": "^2.1.0", - "csstype": "^3.1.2", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -3122,9 +3155,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.13", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz", - "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", + "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0" }, @@ -3135,9 +3168,9 @@ } }, "node_modules/@mui/utils": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.7.tgz", - "integrity": "sha512-8qhsxQRNV6aEOjjSk6YQIYJxkF5klhj8oG1FEEU4z6HV78TjNqRxMP08QGcdsibEbez+nihAaz6vu83b4XqbAg==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", + "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", "dependencies": { "@babel/runtime": "^7.23.9", "@types/prop-types": "^15.7.11", @@ -3161,6 +3194,104 @@ } } }, + "node_modules/@mui/x-date-pickers": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.2.0.tgz", + "integrity": "sha512-hsXugZ+n1ZnHRYzf7+PFrjZ44T+FyGZmTreBmH0M2RUaAblgK+A1V3KNLT+r4Y9gJLH+92LwePxQ9xyfR+E51A==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.24.0", + "@mui/base": "^5.0.0-beta.40", + "@mui/system": "^5.15.14", + "@mui/utils": "^5.15.14", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14", + "date-fns": "^2.25.0 || ^3.2.0", + "date-fns-jalali": "^2.13.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, + "node_modules/@mui/x-date-pickers/node_modules/@mui/base": { + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@next/env": { "version": "14.2.2", "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.2.tgz", @@ -6552,6 +6683,12 @@ "node": ">= 12" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", + "dev": true + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index 7d6c27d7..64ff28cc 100644 --- a/package.json +++ b/package.json @@ -32,11 +32,13 @@ "devDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", + "@mui/x-date-pickers": "^7.2.0", "@playwright/test": "^1.43.1", "@types/jest": "^29.5.11", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.8.0", "all-contributors-cli": "^6.26.1", + "dayjs": "^1.11.10", "eslint": "^8.57.0", "eslint-config-next": "^14.2.2", "eslint-config-prettier": "^9.1.0", diff --git a/packages/frontend/src/retro/components/TimePicker.tsx b/packages/frontend/src/retro/components/TimePicker.tsx deleted file mode 100644 index 91d4dfe3..00000000 --- a/packages/frontend/src/retro/components/TimePicker.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from "react"; -import { FlexBox } from "../../common/components/FlexBox"; -import { TextInput } from "../../common/components/TextInput"; -import IncrementTimerButton from "./buttons/IncrementTimerButton"; - -interface TimePickerProps { - minutes: string; - seconds: string; - disabled: boolean; - isMinutesError: boolean; - isSecondsError: boolean; - onSecondsChange: (event: React.ChangeEvent) => void; - onMinutesChange: (event: React.ChangeEvent) => void; - onSubmit: () => void; - onTimerIncrement: (increment: number) => void; -} - -export function TimePicker({ - minutes, - seconds, - disabled, - isMinutesError, - isSecondsError, - onSecondsChange, - onMinutesChange, - onSubmit, - onTimerIncrement, -}: TimePickerProps) { - return ( - <> - - - - - - - - - - - ); -} diff --git a/packages/frontend/src/retro/components/dialogs/TimerDialog.tsx b/packages/frontend/src/retro/components/dialogs/TimerDialog.tsx index e629d4db..8f139435 100644 --- a/packages/frontend/src/retro/components/dialogs/TimerDialog.tsx +++ b/packages/frontend/src/retro/components/dialogs/TimerDialog.tsx @@ -1,12 +1,13 @@ -import React from "react"; +import React, { useState } from "react"; import { Button, Dialog, DialogActions, DialogContent, DialogTitle } from "@mui/material"; import { CallToActionButton } from "../../../common/components/buttons/CallToActionButton"; import { DialogProps } from "../../../common/types/commonTypes"; -import { TimePicker } from "../TimePicker"; -import { useValidatedTimeInput } from "../../hooks/useValidatedTimeInput"; import { useRetroContext } from "../../context/RetroContext"; import { TimerStatus } from "../../types/retroTypes"; import { calculateMilliseconds } from "../../utils/timerUtils"; +import { LocalizationProvider, TimePicker } from "@mui/x-date-pickers"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import { Dayjs } from "dayjs"; interface TimerDialogProps extends DialogProps { remainingMinutes: number; @@ -19,43 +20,14 @@ export function TimerDialog({ remainingMinutes, remainingSeconds, }: TimerDialogProps) { - const { - retroState, - handleStartTimer, - handleStopTimer, - handlePauseTimer, - handleResumeTimer, - handleChangeTimer, - } = useRetroContext(); + const { retroState, handleStartTimer, handleStopTimer, handlePauseTimer, handleResumeTimer } = + useRetroContext(); const { timerStatus } = retroState; const isTimerRunning = timerStatus === TimerStatus.RUNNING; const isTimerPaused = timerStatus === TimerStatus.PAUSED; const isTimerStopped = timerStatus === TimerStatus.STOPPED; - - const { - value: seconds, - formattedValue: formattedSeconds, - isError: isSecondsError, - onChange: handleSecondsChange, - } = useValidatedTimeInput({ - formatLength: 2, - maxValue: 59, - minValue: 0, - initialValue: 0, - }); - - const { - value: minutes, - formattedValue: formattedMinutes, - isError: isMinutesError, - onChange: handleMinutesChange, - incrementTime: incrementMinutes, - } = useValidatedTimeInput({ - formatLength: 2, - minValue: 0, - maxValue: 99, - initialValue: 5, - }); + const [minutes, setMinutes] = useState(remainingMinutes); + const [seconds, setSeconds] = useState(remainingSeconds); function handleStartClick() { handleStartTimer(calculateMilliseconds(minutes, seconds)); @@ -67,44 +39,31 @@ export function TimerDialog({ close(); } - function handleTimerIncrement(minutes: number) { - if (isTimerStopped) { - incrementMinutes(minutes); - } else { - handleChangeTimer(calculateMilliseconds(remainingMinutes + minutes, remainingSeconds)); - } + function setValue(newValue: Dayjs) { + setMinutes(newValue.minute()); + setSeconds(newValue.second()); } return ( - + Set Timer - + + newValue && setValue(newValue)} + /> + {isTimerRunning && } {isTimerPaused && } - + {isTimerStopped ? "Start" : "Stop"} diff --git a/packages/frontend/src/retro/context/RetroContext.tsx b/packages/frontend/src/retro/context/RetroContext.tsx index 40e786e2..27414da1 100644 --- a/packages/frontend/src/retro/context/RetroContext.tsx +++ b/packages/frontend/src/retro/context/RetroContext.tsx @@ -86,7 +86,6 @@ export interface RetroContextValues { handleCardVotingLimitChanged: (limit: number) => void; handleStartTimer: (duration: number) => void; handlePauseTimer: () => void; - handleChangeTimer: (duration: number) => void; handleStopTimer: () => void; handleResumeTimer: () => void; } @@ -251,10 +250,6 @@ export function RetroContextProvider(props: RetroContextProviderProps) { dispatchAndBroadcast({ type: "START_TIMER", duration: state.timerDuration }); } - function handleChangeTimer(duration: StartTimerAction["duration"]) { - dispatchAndBroadcast({ type: "CHANGE_TIMER", duration }); - } - const resetRetroState = useCallback(() => { dispatch({ type: "SET_RETRO_STATE", payload: initialState }); }, []); @@ -293,7 +288,6 @@ export function RetroContextProvider(props: RetroContextProviderProps) { handleStartTimer, handlePauseTimer, handleStopTimer, - handleChangeTimer, handleResumeTimer, }; diff --git a/packages/frontend/src/retro/reducers/retroReducer.ts b/packages/frontend/src/retro/reducers/retroReducer.ts index d011b1b0..2d12510d 100644 --- a/packages/frontend/src/retro/reducers/retroReducer.ts +++ b/packages/frontend/src/retro/reducers/retroReducer.ts @@ -240,9 +240,6 @@ export const retroReducer = (state: RetroState, action: RetroAction): RetroState case "PAUSE_TIMER": { return { ...state, timerStatus: TimerStatus.PAUSED }; } - case "CHANGE_TIMER": { - return { ...state, timerDuration: action.duration }; - } case "DISCONNECT": { const { participants, waitingList } = state; const disconnectedUserId = action.payload; diff --git a/packages/frontend/src/retro/types/retroActions.ts b/packages/frontend/src/retro/types/retroActions.ts index 61293cd6..24b162f2 100644 --- a/packages/frontend/src/retro/types/retroActions.ts +++ b/packages/frontend/src/retro/types/retroActions.ts @@ -113,11 +113,6 @@ export interface StopTimerAction extends BaseAction { type: "STOP_TIMER"; } -export interface ChangeTimerAction extends BaseAction { - type: "CHANGE_TIMER"; - duration: number; -} - export type RetroAction = | PeerToPeerAction | CardUpvoteAction @@ -142,5 +137,4 @@ export type RetroAction = | CardVotingLimitChangedAction | StartTimerAction | PauseTimerAction - | StopTimerAction - | ChangeTimerAction; + | StopTimerAction;