diff --git a/package-lock.json b/package-lock.json index 1b882d4..1663d01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@types/lodash": "^4.14.196", "@types/react-webcam": "^3.0.0", "axios": "^1.4.0", + "classnames": "^2.3.2", "dotenv": "^16.3.1", "jest": "^29.5.0", "lib-jitsi-meet": "^1.0.6", @@ -34,6 +35,7 @@ "eslint": "^8.38.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.3.4", + "husky": "^8.0.3", "jsdom": "^22.1.0", "typescript": "^5.0.2", "vite": "^4.3.9", @@ -4312,6 +4314,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -5401,6 +5408,21 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", diff --git a/package.json b/package.json index 50524cb..9549d4b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "dev": "vite", "build": "tsc && vite build", "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" + "preview": "vite preview", + "prepare": "husky install" }, "dependencies": { "@auth0/auth0-react": "^2.1.1", @@ -15,6 +16,7 @@ "@types/lodash": "^4.14.196", "@types/react-webcam": "^3.0.0", "axios": "^1.4.0", + "classnames": "^2.3.2", "dotenv": "^16.3.1", "jest": "^29.5.0", "lib-jitsi-meet": "^1.0.6", @@ -36,6 +38,7 @@ "eslint": "^8.38.0", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.3.4", + "husky": "^8.0.3", "jsdom": "^22.1.0", "typescript": "^5.0.2", "vite": "^4.3.9", diff --git a/public/icons/check_small.svg b/public/icons/check_small.svg new file mode 100644 index 0000000..559ca06 --- /dev/null +++ b/public/icons/check_small.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/index.css b/src/app/index.css index 4fc75bd..3731cd0 100644 --- a/src/app/index.css +++ b/src/app/index.css @@ -60,6 +60,11 @@ input::-ms-clear { display: none; } +input[type="checkbox"] { + -webkit-appearance: none; + appearance: none; +} + button { cursor: pointer; } diff --git a/src/features/ControllersMenu/ui/MenuController.tsx b/src/features/ControllersMenu/ui/MenuController.tsx index 9460c94..e171e3e 100644 --- a/src/features/ControllersMenu/ui/MenuController.tsx +++ b/src/features/ControllersMenu/ui/MenuController.tsx @@ -1,14 +1,38 @@ -import React from 'react'; -import ControllerLayout from '../../../widgets/layout/ControllerLayout'; +import React from "react"; +import ControllerLayout from "../../../widgets/layout/ControllerLayout"; +import settingsState from "../../../pages/CallPageCustomUI/store/settingsState"; export const MenuController: React.FC = () => { + function openSettings() { + settingsState.openSettings(); + } + return ( - - - - - - + + + + + - ) -} + ); +}; diff --git a/src/features/DeviceSetting/ui/SelectDevice.tsx b/src/features/DeviceSetting/ui/SelectDevice.tsx index 26c0c57..0c8d67a 100644 --- a/src/features/DeviceSetting/ui/SelectDevice.tsx +++ b/src/features/DeviceSetting/ui/SelectDevice.tsx @@ -76,6 +76,10 @@ const Divider = styled.div` interface Props { title: string; list: string[]; + stylesText?: Object; + stylesBlock?: Object; + stylesList?: Object; + stylesContainer?: Object; } export const SelectDevice: React.FC = (Props) => { @@ -83,20 +87,29 @@ export const SelectDevice: React.FC = (Props) => { const [selected, setSelected] = useState("По-умолчанию"); return ( - - {Props.title} - setSelectActive(!selectActive)}> + + {Props.title} + setSelectActive(!selectActive)} + style={Props.stylesBlock} + > {selected} {selectActive ? : } -
+
{Props.list.length ? ( @@ -118,7 +131,9 @@ export const SelectDevice: React.FC = (Props) => { )) ) : ( - Нет устройств + + Нет устройств + )}
diff --git a/src/features/Function/ui/FunctionBlock.tsx b/src/features/Function/ui/FunctionBlock.tsx index c795b7b..2f0de96 100644 --- a/src/features/Function/ui/FunctionBlock.tsx +++ b/src/features/Function/ui/FunctionBlock.tsx @@ -56,6 +56,8 @@ export const FunctionBlock: React.FC = ({ func }) => { : setCurrentIcon(func.icon); } + console.log(func.link); + return ( diff --git a/src/pages/CallPageCustomUI/CallPage.tsx b/src/pages/CallPageCustomUI/CallPage.tsx index 89f2f30..6d80595 100644 --- a/src/pages/CallPageCustomUI/CallPage.tsx +++ b/src/pages/CallPageCustomUI/CallPage.tsx @@ -8,6 +8,8 @@ import avatar from "../../../public/icons/avatar.svg"; import Sidebar from "../../widgets/layout/Sidebar/Sidebar"; import storeSidebar from "./store/sidebarState"; import { observer } from "mobx-react-lite"; +import settingsState from "./store/settingsState"; +import SettingsCallPopup from "../../widgets/layout/SettingsCallPopup/SettingsCallPopup"; const Container = styled.div` width: 100%; @@ -30,6 +32,17 @@ const Content = styled.div` transition: all 0.3s ease-out; `; +const Overlay = styled.div` + background: white; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + opacity: 0.4; + z-index: 100; +`; + const peopleList: IUser[] = [ { email: "jjj", @@ -178,6 +191,10 @@ const peopleList: IUser[] = [ ]; const CallPage: React.FC = observer(() => { + function closePopup() { + settingsState.closeSettings(); + } + return ( { } > - + + {settingsState.isActive && } + {settingsState.isActive && } ); }); diff --git a/src/pages/CallPageCustomUI/store/settingsState.ts b/src/pages/CallPageCustomUI/store/settingsState.ts new file mode 100644 index 0000000..0eddd3d --- /dev/null +++ b/src/pages/CallPageCustomUI/store/settingsState.ts @@ -0,0 +1,28 @@ +import { makeAutoObservable } from "mobx"; + +class SettingsState { + isActive = false; + selected = "My"; + + constructor() { + makeAutoObservable(this); + } + + openSettings() { + this.isActive = true; + } + + closeSettings() { + this.isActive = false; + } + + selectClass() { + this.selected = "Class"; + } + + selectMy() { + this.selected = "My"; + } +} + +export default new SettingsState(); diff --git a/src/shared/ui/checkBox/CheckBox.tsx b/src/shared/ui/checkBox/CheckBox.tsx new file mode 100644 index 0000000..9d09a79 --- /dev/null +++ b/src/shared/ui/checkBox/CheckBox.tsx @@ -0,0 +1,58 @@ +import React, { useState } from "react"; +import styled from "styled-components"; +import check from "../../../../public/icons/check_small.svg"; + +const Container = styled.div` + display: flex; + align-items: center; + gap: 15px; +`; + +const Input = styled.input` + width: 18px; + height: 18px; + border-radius: 2px; + border: 1.5px solid var(--grey-4, #c5ccd5); + + &:checked { + background: var(--blue, #175ef1) url(${check}) center no-repeat; + } + &:hover { + cursor: pointer; + } +`; + +const Title = styled.div` + color: var(--grey-3, #a0afc1); + font-family: var(--font); + font-size: 16px; + font-weight: 400; +`; + +interface Props { + text: string; +} + +const CheckBox: React.FC = ({ text }) => { + const [selected, setSelected] = useState(true); + + function changeSelected() { + selected ? setSelected(false) : setSelected(true); + } + + return ( + + + + {text} + + + ); +}; + +export default CheckBox; diff --git a/src/shared/ui/switchButton/SwitchButton.tsx b/src/shared/ui/switchButton/SwitchButton.tsx new file mode 100644 index 0000000..19e1161 --- /dev/null +++ b/src/shared/ui/switchButton/SwitchButton.tsx @@ -0,0 +1,20 @@ +import React, { useState } from "react"; +import styles from "./styles.module.css"; +import classNames from "classnames"; + +const SwitchButton: React.FC = () => { + const [selected, setSelected] = useState(true); + + function changeSelected() { + selected ? setSelected(false) : setSelected(true); + } + + return ( +
+ ); +}; + +export default SwitchButton; diff --git a/src/shared/ui/switchButton/styles.module.css b/src/shared/ui/switchButton/styles.module.css new file mode 100644 index 0000000..335aa23 --- /dev/null +++ b/src/shared/ui/switchButton/styles.module.css @@ -0,0 +1,28 @@ +.switchBtn { + display: inline-block; + width: 36px; + height: 20px; + border-radius: 15px; + background: var(--grey_4); + cursor: pointer; + position: relative; + transition-duration: 300ms; +} +.switchBtn::after { + content: ""; + height: 17px; + width: 16px; + border-radius: 50%; + background: #fff; + top: 1.5px; + left: 1.5px; + transition-duration: 300ms; + position: absolute; + z-index: 1; +} +.switchOn { + background: var(--blue); +} +.switchOn::after { + left: 18.5px; +} diff --git a/src/widgets/CallControllers.tsx b/src/widgets/CallControllers.tsx index d7c4a1f..b08a0ee 100644 --- a/src/widgets/CallControllers.tsx +++ b/src/widgets/CallControllers.tsx @@ -1,42 +1,42 @@ -import React from 'react'; -import styled from 'styled-components'; -import ControllersWrapper from './layout/ControllersWrapper'; -import { MicrophoneController } from '../features/MicrophoneController'; -import { CameraController } from '../features/CameraController'; -import { RecordController } from '../features/RecordController'; -import { HandController } from '../features/HandController/ui/HandController'; -import { ScreenController } from '../features/ScreenController'; -import { MenuController } from '../features/ControllersMenu'; -import { LeaveCall } from '../features/LeaveCall'; +import React from "react"; +import styled from "styled-components"; +import ControllersWrapper from "./layout/ControllersWrapper"; +import { MicrophoneController } from "../features/MicrophoneController"; +import { CameraController } from "../features/CameraController"; +import { RecordController } from "../features/RecordController"; +import { HandController } from "../features/HandController/ui/HandController"; +import { ScreenController } from "../features/ScreenController"; +import { MenuController } from "../features/ControllersMenu"; +import { LeaveCall } from "../features/LeaveCall"; const Left = styled.div` display: flex; gap: 20px; justify-content: space-between; -` +`; const Center = styled.div` display: flex; gap: 20px; justify-content: space-between; -` +`; const CallControllers: React.FC = () => { return ( - - - - -
- - - - -
- + + + + +
+ + + + +
+
- ) -} + ); +}; -export default CallControllers \ No newline at end of file +export default CallControllers; diff --git a/src/widgets/SettingsClass/SettingsClass.tsx b/src/widgets/SettingsClass/SettingsClass.tsx new file mode 100644 index 0000000..c7dd78b --- /dev/null +++ b/src/widgets/SettingsClass/SettingsClass.tsx @@ -0,0 +1,70 @@ +import React from "react"; +import styled from "styled-components"; +import { SettingClassLayout } from "../layout/SettingClassLayout"; +import SwitchButton from "../../shared/ui/switchButton/SwitchButton"; + +const Setting = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + gap: 15px; +`; + +const SettingText = styled.div` + color: var(--black, #000); + font-family: var(--font); + font-size: 16px; + font-weight: 400; + line-height: normal; +`; + +const SettingsClass: React.FC = () => { + return ( + <> + + <> + + + Разрешить участникам видеть все демонстрации + + + + + + Разрешить участникам выбирать вкладку для показа + + + + + + + <> + + Разрешить участникам использовать камеру + + + + + + <> + + + Разрешить участникам использовать микрофон + + + + + + + <> + + Разрешить участникам записывать встречи + + + + + + ); +}; + +export default SettingsClass; diff --git a/src/widgets/SettingsMy/SettingsMy.tsx b/src/widgets/SettingsMy/SettingsMy.tsx new file mode 100644 index 0000000..1ccde53 --- /dev/null +++ b/src/widgets/SettingsMy/SettingsMy.tsx @@ -0,0 +1,104 @@ +import React, { useState } from "react"; +import styled from "styled-components"; +import { SelectDevice } from "../../features/DeviceSetting"; +import { useAuth0 } from "@auth0/auth0-react"; + +const Input = styled.input` + display: flex; + width: 100%; + min-height: 54px; + padding: 4px 0px 4px 16px; + align-items: center; + border-radius: 10px; + background: #fff; + border: 1.5px solid var(--blue); + margin-bottom: 10px; + color: var(--black, #000); + font-family: var(--font); + font-size: 20px; + font-weight: 400; + line-height: 24px; + letter-spacing: 0.5px; +`; + +const Title = styled.label` + position: absolute; + top: -9px; + left: 14px; + color: var(--blue, #175ef1); + background: white; + padding: 0 5px; + font-family: var(--font); + font-size: 18px; + font-weight: 400; + line-height: 16px; + letter-spacing: 0.4px; +`; + +const stylesText = { + color: "var(--black, #000)", + fontSize: "20px", + fontWeight: "600", + lineHeight: "normal", +}; + +const stylesBlock = { + border: "1px solid var(--grey-5, #D5DEE8)", + boxShadow: "none", +}; + +const stylesList = { + border: "1px solid var(--grey-5, #D5DEE8)", + boxShadow: "none", +}; + +const stylesContainer = { + flex: "0 0", +}; + +const SettingsMy: React.FC = () => { + const { user } = useAuth0(); + const [name, setName] = useState(user?.name); + + return ( + <> +
+ Имя в классе + ) => + setName(e.target.value) + } + /> +
+ + + + + ); +}; + +export default SettingsMy; diff --git a/src/widgets/UserGrid/UserGrid.tsx b/src/widgets/UserGrid/UserGrid.tsx index fce7228..bd069e7 100644 --- a/src/widgets/UserGrid/UserGrid.tsx +++ b/src/widgets/UserGrid/UserGrid.tsx @@ -64,7 +64,7 @@ const ShowMoreButton = styled.button` position: absolute; top: 50%; transform: translate(0, -50%); - z-index: 1000; + z-index: 10; &:hover { cursor: pointer; diff --git a/src/widgets/layout/Header.tsx b/src/widgets/layout/Header.tsx index 1bcf9b1..243c5cd 100644 --- a/src/widgets/layout/Header.tsx +++ b/src/widgets/layout/Header.tsx @@ -14,9 +14,7 @@ const Container = styled.div` background-color: var(--white); `; -const Left = styled.div` - -`; +const Left = styled.div``; const Logo = styled.a` text-align: center; diff --git a/src/widgets/layout/SettingClassLayout.tsx b/src/widgets/layout/SettingClassLayout.tsx new file mode 100644 index 0000000..6b3cff0 --- /dev/null +++ b/src/widgets/layout/SettingClassLayout.tsx @@ -0,0 +1,52 @@ +import React from "react"; +import { styled } from "styled-components"; +import CheckBox from "../../shared/ui/checkBox/CheckBox"; + +const Container = styled.div` + width: 100%; + display: flex; + padding-bottom: 20px; + flex-direction: column; + align-items: flex-start; + gap: 20px; +`; + +const Head = styled.div` + display: flex; + width: 100%; + justify-content: space-between; + align-items: center; + gap: 100px; +`; + +const Title = styled.div` + color: var(--black, #000); + font-family: var(--font); + font-size: 20px; + font-weight: 600; + line-height: normal; +`; + +const Main = styled.div` + display: flex; + width: 100%; + flex-direction: column; + gap: 20px; +`; + +interface Props { + title: string; + children?: React.ReactNode; +} + +export const SettingClassLayout: React.FC = ({ title, children }) => { + return ( + + + {title} + + +
{children}
+
+ ); +}; diff --git a/src/widgets/layout/SettingsCallPopup/SettingsCallPopup.tsx b/src/widgets/layout/SettingsCallPopup/SettingsCallPopup.tsx new file mode 100644 index 0000000..e25f357 --- /dev/null +++ b/src/widgets/layout/SettingsCallPopup/SettingsCallPopup.tsx @@ -0,0 +1,133 @@ +import React from "react"; +import styled from "styled-components"; +import settingsState from "../../../pages/CallPageCustomUI/store/settingsState"; +import { observer } from "mobx-react-lite"; +import FormButton from "../../../shared/ui/formButton/FormButton"; +import SettingsMy from "../../SettingsMy/SettingsMy"; +import SettingsClass from "../../SettingsClass/SettingsClass"; + +const Container = styled.div` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + min-width: 530px; + max-height: 100vh; + padding: 30px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 30px; + border-radius: 8px; + background: var(--white, #fff); + box-shadow: 0px 0px 2px 0px #c5ccd5; + z-index: 101; +`; + +const Header = styled.div` + display: flex; + justify-content: center; + align-items: center; + gap: 10px; + width: 100%; + padding-bottom: 20px; + border-bottom: 0.5px solid var(--grey-5, #d5dee8); +`; + +const HeaderText = styled.div` + display: flex; + padding: 8px 16px; + align-items: flex-start; + gap: 10px; + color: var(--grey-1, #5f6a77); + font-family: var(--font); + font-size: 20px; + font-weight: 400; + line-height: normal; + + &:hover { + cursor: pointer; + } +`; + +const Settings = styled.form` + overflow: auto; + width: 100%; + display: flex; + flex-direction: column; + align-items: stretch; + gap: 20px; + padding-bottom: 5px; + + &::-webkit-scrollbar { + width: 0; + } +`; + +const Buttons = styled.div` + display: flex; + justify-content: center; + align-items: center; + gap: 30px; +`; + +const Back = styled.button` + color: var(--grey-1, #5f6a77); + font-family: var(--font); + font-size: 20px; + font-weight: 400; + line-height: normal; +`; + +const styleSelected = { + color: "var(--blue, #175EF1)", + fontWeight: 600, +}; + +const SettingsCallPopup: React.FC = observer(() => { + function openMy() { + settingsState.selectMy(); + } + + function openClass() { + settingsState.selectClass(); + } + + function saveChanges() { + settingsState.closeSettings(); + } + + function backChanges() { + settingsState.closeSettings(); + } + + return ( + +
+ + Мои настройки + + + Настройки класса + +
+ + {settingsState.selected === "My" && } + {settingsState.selected === "Class" && } + + Сохранить + Отмена + + +
+ ); +}); + +export default SettingsCallPopup;